From 4b69d4c3d7c133ebc9393ef3f86ce38831921cb6 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 21 Nov 2018 17:44:15 +0100 Subject: qapi: Fix string-input-visitor to reject NaN and infinities The string-input-visitor happily accepts NaN and infinities when parsing numbers (doubles). They shouldn't. Fix that. Also, add two test cases, testing if "NaN" and "inf" is properly rejected. Reviewed-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: David Hildenbrand Message-Id: <20181121164421.20780-4-david@redhat.com> Signed-off-by: Markus Armbruster --- tests/test-string-input-visitor.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'tests/test-string-input-visitor.c') diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 88e0e1aa9a..1efba06948 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -252,6 +252,19 @@ static void test_visitor_in_number(TestInputVisitorData *data, visit_type_number(v, NULL, &res, &err); g_assert(!err); g_assert_cmpfloat(res, ==, value); + + /* NaN and infinity has to be rejected */ + + v = visitor_input_test_init(data, "NaN"); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); + + v = visitor_input_test_init(data, "inf"); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); + } static void test_visitor_in_string(TestInputVisitorData *data, -- cgit v1.2.3-55-g7522 From eac475410e9513b43a3ee0af9dfa22ab49230a71 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 21 Nov 2018 17:44:17 +0100 Subject: test-string-input-visitor: Add more tests Test that very big/small values are not accepted and that ranges with only one element work. Also test that ranges are ascending and cannot have more than 65536 elements. Rename expect4 to expect5, as we will be moving that to a separate ulist test after the rework. Reviewed-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: David Hildenbrand Message-Id: <20181121164421.20780-6-david@redhat.com> Signed-off-by: Markus Armbruster --- tests/test-string-input-visitor.c | 41 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'tests/test-string-input-visitor.c') diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 1efba06948..8ee0d1b284 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -121,7 +121,8 @@ static void test_visitor_in_intList(TestInputVisitorData *data, int64_t expect1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20 }; int64_t expect2[] = { 32767, -32768, -32767 }; int64_t expect3[] = { INT64_MAX, INT64_MIN }; - uint64_t expect4[] = { UINT64_MAX }; + int64_t expect4[] = { 1 }; + uint64_t expect5[] = { UINT64_MAX }; Error *err = NULL; int64List *res = NULL; int64List *tail; @@ -140,8 +141,44 @@ static void test_visitor_in_intList(TestInputVisitorData *data, "-9223372036854775808,9223372036854775807"); check_ilist(v, expect3, ARRAY_SIZE(expect3)); + v = visitor_input_test_init(data, "1-1"); + check_ilist(v, expect4, ARRAY_SIZE(expect4)); + v = visitor_input_test_init(data, "18446744073709551615"); - check_ulist(v, expect4, ARRAY_SIZE(expect4)); + check_ulist(v, expect5, ARRAY_SIZE(expect5)); + + /* Value too large */ + + v = visitor_input_test_init(data, "9223372036854775808"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Value too small */ + + v = visitor_input_test_init(data, "-9223372036854775809"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range not ascending */ + + v = visitor_input_test_init(data, "3-1"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + v = visitor_input_test_init(data, "9223372036854775807-0"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range too big (65536 is the limit against DOS attacks) */ + + v = visitor_input_test_init(data, "0-65536"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); /* Empty list */ -- cgit v1.2.3-55-g7522 From c9fba9de89db51a07689e2cba4865a1e564b8f0f Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 21 Nov 2018 17:44:18 +0100 Subject: qapi: Rewrite string-input-visitor's integer and list parsing The input visitor has some problems right now, especially - unsigned type "Range" is used to process signed ranges, resulting in inconsistent behavior and ugly/magical code - uint64_t are parsed like int64_t, so big uint64_t values are not supported and error messages are misleading - lists/ranges of int64_t are accepted although no list is parsed and we should rather report an error - lists/ranges are preparsed using int64_t, making it hard to implement uint64_t values or uint64_t lists - types that don't support lists don't bail out - visiting beyond the end of a list is not handled properly - we don't actually parse lists, we parse *sets*: members are sorted, and duplicates eliminated So let's rewrite it by getting rid of usage of the type "Range" and properly supporting lists of int64_t and uint64_t (including ranges of both types), fixing the above mentioned issues. Lists of other types are not supported and will properly report an error. Virtual walks are now supported. Tests have to be fixed up: - Two BUGs were hardcoded that are fixed now - The string-input-visitor now actually returns a parsed list and not an ordered set. Please note that no users/callers have to be fixed up. Candidates using visit_type_uint16List() and friends are: - backends/hostmem.c:host_memory_backend_set_host_nodes() -- Code can deal with duplicates/unsorted lists - numa.c::query_memdev() -- via object_property_get_uint16List(), the list will still be sorted and without duplicates (via host_memory_backend_get_host_nodes()) - qapi-visit.c::visit_type_Memdev_members() - qapi-visit.c::visit_type_NumaNodeOptions_members() - qapi-visit.c::visit_type_RockerOfDpaGroup_members - qapi-visit.c::visit_type_RxFilterInfo_members() -- Not used with string-input-visitor. Reviewed-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: David Hildenbrand Message-Id: <20181121164421.20780-7-david@redhat.com> Signed-off-by: Markus Armbruster --- include/qapi/string-input-visitor.h | 4 +- qapi/string-input-visitor.c | 405 ++++++++++++++++++++---------------- tests/test-string-input-visitor.c | 18 +- 3 files changed, 234 insertions(+), 193 deletions(-) (limited to 'tests/test-string-input-visitor.c') diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h index 33551340e3..921f3875b9 100644 --- a/include/qapi/string-input-visitor.h +++ b/include/qapi/string-input-visitor.h @@ -19,8 +19,8 @@ typedef struct StringInputVisitor StringInputVisitor; /* * The string input visitor does not implement support for visiting - * QAPI structs, alternates, null, or arbitrary QTypes. It also - * requires a non-null list argument to visit_start_list(). + * QAPI structs, alternates, null, or arbitrary QTypes. Only flat lists + * of integers (except type "size") are supported. */ Visitor *string_input_visitor_new(const char *str); diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index b89c6c4e06..bd92080667 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -4,10 +4,10 @@ * Copyright Red Hat, Inc. 2012-2016 * * Author: Paolo Bonzini + * David Hildenbrand * * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. - * */ #include "qemu/osdep.h" @@ -18,21 +18,42 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/qnull.h" #include "qemu/option.h" -#include "qemu/queue.h" -#include "qemu/range.h" #include "qemu/cutils.h" +typedef enum ListMode { + /* no list parsing active / no list expected */ + LM_NONE, + /* we have an unparsed string remaining */ + LM_UNPARSED, + /* we have an unfinished int64 range */ + LM_INT64_RANGE, + /* we have an unfinished uint64 range */ + LM_UINT64_RANGE, + /* we have parsed the string completely and no range is remaining */ + LM_END, +} ListMode; + +/* protect against DOS attacks, limit the amount of elements per range */ +#define RANGE_MAX_ELEMENTS 65536 + +typedef union RangeElement { + int64_t i64; + uint64_t u64; +} RangeElement; struct StringInputVisitor { Visitor visitor; - GList *ranges; - GList *cur_range; - int64_t cur; + /* List parsing state */ + ListMode lm; + RangeElement rangeNext; + RangeElement rangeEnd; + const char *unparsed_string; + void *list; + /* The original string to parse */ const char *string; - void *list; /* Only needed for sanity checking the caller */ }; static StringInputVisitor *to_siv(Visitor *v) @@ -40,136 +61,42 @@ static StringInputVisitor *to_siv(Visitor *v) return container_of(v, StringInputVisitor, visitor); } -static void free_range(void *range, void *dummy) -{ - g_free(range); -} - -static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) -{ - char *str = (char *) siv->string; - long long start, end; - Range *cur; - char *endptr; - - if (siv->ranges) { - return 0; - } - - if (!*str) { - return 0; - } - - do { - errno = 0; - start = strtoll(str, &endptr, 0); - if (errno == 0 && endptr > str) { - if (*endptr == '\0') { - cur = g_malloc0(sizeof(*cur)); - range_set_bounds(cur, start, start); - siv->ranges = range_list_insert(siv->ranges, cur); - cur = NULL; - str = NULL; - } else if (*endptr == '-') { - str = endptr + 1; - errno = 0; - end = strtoll(str, &endptr, 0); - if (errno == 0 && endptr > str && start <= end && - (start > INT64_MAX - 65536 || - end < start + 65536)) { - if (*endptr == '\0') { - cur = g_malloc0(sizeof(*cur)); - range_set_bounds(cur, start, end); - siv->ranges = range_list_insert(siv->ranges, cur); - cur = NULL; - str = NULL; - } else if (*endptr == ',') { - str = endptr + 1; - cur = g_malloc0(sizeof(*cur)); - range_set_bounds(cur, start, end); - siv->ranges = range_list_insert(siv->ranges, cur); - cur = NULL; - } else { - goto error; - } - } else { - goto error; - } - } else if (*endptr == ',') { - str = endptr + 1; - cur = g_malloc0(sizeof(*cur)); - range_set_bounds(cur, start, start); - siv->ranges = range_list_insert(siv->ranges, cur); - cur = NULL; - } else { - goto error; - } - } else { - goto error; - } - } while (str); - - return 0; -error: - g_list_foreach(siv->ranges, free_range, NULL); - g_list_free(siv->ranges); - siv->ranges = NULL; - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "an int64 value or range"); - return -1; -} - -static void -start_list(Visitor *v, const char *name, GenericList **list, size_t size, - Error **errp) +static void start_list(Visitor *v, const char *name, GenericList **list, + size_t size, Error **errp) { StringInputVisitor *siv = to_siv(v); - /* We don't support visits without a list */ - assert(list); + assert(siv->lm == LM_NONE); siv->list = list; + siv->unparsed_string = siv->string; - if (parse_str(siv, name, errp) < 0) { - *list = NULL; - return; - } - - siv->cur_range = g_list_first(siv->ranges); - if (siv->cur_range) { - Range *r = siv->cur_range->data; - if (r) { - siv->cur = range_lob(r); + if (!siv->string[0]) { + if (list) { + *list = NULL; } - *list = g_malloc0(size); + siv->lm = LM_END; } else { - *list = NULL; + if (list) { + *list = g_malloc0(size); + } + siv->lm = LM_UNPARSED; } } static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) { StringInputVisitor *siv = to_siv(v); - Range *r; - - if (!siv->ranges || !siv->cur_range) { - return NULL; - } - r = siv->cur_range->data; - if (!r) { + switch (siv->lm) { + case LM_END: return NULL; - } - - if (!range_contains(r, siv->cur)) { - siv->cur_range = g_list_next(siv->cur_range); - if (!siv->cur_range) { - return NULL; - } - r = siv->cur_range->data; - if (!r) { - return NULL; - } - siv->cur = range_lob(r); + case LM_INT64_RANGE: + case LM_UINT64_RANGE: + case LM_UNPARSED: + /* we have an unparsed string or something left in a range */ + break; + default: + abort(); } tail->next = g_malloc0(size); @@ -179,88 +106,208 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) static void check_list(Visitor *v, Error **errp) { const StringInputVisitor *siv = to_siv(v); - Range *r; - GList *cur_range; - if (!siv->ranges || !siv->cur_range) { + switch (siv->lm) { + case LM_INT64_RANGE: + case LM_UINT64_RANGE: + case LM_UNPARSED: + error_setg(errp, "Fewer list elements expected"); return; - } - - r = siv->cur_range->data; - if (!r) { + case LM_END: return; + default: + abort(); } - - if (!range_contains(r, siv->cur)) { - cur_range = g_list_next(siv->cur_range); - if (!cur_range) { - return; - } - r = cur_range->data; - if (!r) { - return; - } - } - - error_setg(errp, "Range contains too many values"); } static void end_list(Visitor *v, void **obj) { StringInputVisitor *siv = to_siv(v); + assert(siv->lm != LM_NONE); assert(siv->list == obj); + siv->list = NULL; + siv->unparsed_string = NULL; + siv->lm = LM_NONE; +} + +static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj) +{ + const char *endptr; + int64_t start, end; + + /* parse a simple int64 or range */ + if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) { + return -EINVAL; + } + end = start; + + switch (endptr[0]) { + case '\0': + siv->unparsed_string = endptr; + break; + case ',': + siv->unparsed_string = endptr + 1; + break; + case '-': + /* parse the end of the range */ + if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) { + return -EINVAL; + } + if (start > end || end - start >= RANGE_MAX_ELEMENTS) { + return -EINVAL; + } + switch (endptr[0]) { + case '\0': + siv->unparsed_string = endptr; + break; + case ',': + siv->unparsed_string = endptr + 1; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + /* we have a proper range (with maybe only one element) */ + siv->lm = LM_INT64_RANGE; + siv->rangeNext.i64 = start; + siv->rangeEnd.i64 = end; + return 0; } static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) { StringInputVisitor *siv = to_siv(v); - - if (parse_str(siv, name, errp) < 0) { + int64_t val; + + switch (siv->lm) { + case LM_NONE: + /* just parse a simple int64, bail out if not completely consumed */ + if (qemu_strtoi64(siv->string, NULL, 0, &val)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + name ? name : "null", "int64"); + return; + } + *obj = val; return; + case LM_UNPARSED: + if (try_parse_int64_list_entry(siv, obj)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "list of int64 values or ranges"); + return; + } + assert(siv->lm == LM_INT64_RANGE); + /* fall through */ + case LM_INT64_RANGE: + /* return the next element in the range */ + assert(siv->rangeNext.i64 <= siv->rangeEnd.i64); + *obj = siv->rangeNext.i64++; + + if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) { + /* end of range, check if there is more to parse */ + siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; + } + return; + case LM_END: + error_setg(errp, "Fewer list elements expected"); + return; + default: + abort(); } +} - if (!siv->ranges) { - goto error; - } - - if (!siv->cur_range) { - Range *r; +static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj) +{ + const char *endptr; + uint64_t start, end; - siv->cur_range = g_list_first(siv->ranges); - if (!siv->cur_range) { - goto error; + /* parse a simple uint64 or range */ + if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) { + return -EINVAL; + } + end = start; + + switch (endptr[0]) { + case '\0': + siv->unparsed_string = endptr; + break; + case ',': + siv->unparsed_string = endptr + 1; + break; + case '-': + /* parse the end of the range */ + if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) { + return -EINVAL; } - - r = siv->cur_range->data; - if (!r) { - goto error; + if (start > end || end - start >= RANGE_MAX_ELEMENTS) { + return -EINVAL; } - - siv->cur = range_lob(r); + switch (endptr[0]) { + case '\0': + siv->unparsed_string = endptr; + break; + case ',': + siv->unparsed_string = endptr + 1; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; } - *obj = siv->cur; - siv->cur++; - return; - -error: - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "an int64 value or range"); + /* we have a proper range (with maybe only one element) */ + siv->lm = LM_UINT64_RANGE; + siv->rangeNext.u64 = start; + siv->rangeEnd.u64 = end; + return 0; } static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) { - /* FIXME: parse_type_int64 mishandles values over INT64_MAX */ - int64_t i; - Error *err = NULL; - parse_type_int64(v, name, &i, &err); - if (err) { - error_propagate(errp, err); - } else { - *obj = i; + StringInputVisitor *siv = to_siv(v); + uint64_t val; + + switch (siv->lm) { + case LM_NONE: + /* just parse a simple uint64, bail out if not completely consumed */ + if (qemu_strtou64(siv->string, NULL, 0, &val)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "uint64"); + return; + } + *obj = val; + return; + case LM_UNPARSED: + if (try_parse_uint64_list_entry(siv, obj)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "list of uint64 values or ranges"); + return; + } + assert(siv->lm == LM_UINT64_RANGE); + /* fall through */ + case LM_UINT64_RANGE: + /* return the next element in the range */ + assert(siv->rangeNext.u64 <= siv->rangeEnd.u64); + *obj = siv->rangeNext.u64++; + + if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) { + /* end of range, check if there is more to parse */ + siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; + } + return; + case LM_END: + error_setg(errp, "Fewer list elements expected"); + return; + default: + abort(); } } @@ -271,6 +318,7 @@ static void parse_type_size(Visitor *v, const char *name, uint64_t *obj, Error *err = NULL; uint64_t val; + assert(siv->lm == LM_NONE); parse_option_size(name, siv->string, &val, &err); if (err) { error_propagate(errp, err); @@ -285,6 +333,7 @@ static void parse_type_bool(Visitor *v, const char *name, bool *obj, { StringInputVisitor *siv = to_siv(v); + assert(siv->lm == LM_NONE); if (!strcasecmp(siv->string, "on") || !strcasecmp(siv->string, "yes") || !strcasecmp(siv->string, "true")) { @@ -307,6 +356,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj, { StringInputVisitor *siv = to_siv(v); + assert(siv->lm == LM_NONE); *obj = g_strdup(siv->string); } @@ -316,6 +366,7 @@ static void parse_type_number(Visitor *v, const char *name, double *obj, StringInputVisitor *siv = to_siv(v); double val; + assert(siv->lm == LM_NONE); if (qemu_strtod_finite(siv->string, NULL, &val)) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "number"); @@ -330,9 +381,10 @@ static void parse_type_null(Visitor *v, const char *name, QNull **obj, { StringInputVisitor *siv = to_siv(v); + assert(siv->lm == LM_NONE); *obj = NULL; - if (!siv->string || siv->string[0]) { + if (siv->string[0]) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "null"); return; @@ -345,8 +397,6 @@ static void string_input_free(Visitor *v) { StringInputVisitor *siv = to_siv(v); - g_list_foreach(siv->ranges, free_range, NULL); - g_list_free(siv->ranges); g_free(siv); } @@ -372,5 +422,6 @@ Visitor *string_input_visitor_new(const char *str) v->visitor.free = string_input_free; v->string = str; + v->lm = LM_NONE; return &v->visitor; } diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 8ee0d1b284..c5379365a6 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -92,16 +92,6 @@ static void check_ulist(Visitor *v, uint64_t *expected, size_t n) uint64List *tail; int i; - /* BUG: unsigned numbers above INT64_MAX don't work */ - for (i = 0; i < n; i++) { - if (expected[i] > INT64_MAX) { - Error *err = NULL; - visit_type_uint64List(v, NULL, &res, &err); - error_free_or_abort(&err); - return; - } - } - visit_type_uint64List(v, NULL, &res, &error_abort); tail = res; for (i = 0; i < n; i++) { @@ -117,10 +107,10 @@ static void check_ulist(Visitor *v, uint64_t *expected, size_t n) static void test_visitor_in_intList(TestInputVisitorData *data, const void *unused) { - /* Note: the visitor *sorts* ranges *unsigned* */ - int64_t expect1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20 }; + int64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7, + 8, 9, 1, 2, 3, 4, 5, 6, 7, 8 }; int64_t expect2[] = { 32767, -32768, -32767 }; - int64_t expect3[] = { INT64_MAX, INT64_MIN }; + int64_t expect3[] = { INT64_MIN, INT64_MAX }; int64_t expect4[] = { 1 }; uint64_t expect5[] = { UINT64_MAX }; Error *err = NULL; @@ -226,7 +216,7 @@ static void test_visitor_in_intList(TestInputVisitorData *data, visit_type_int64(v, NULL, &tail->value, &err); g_assert_cmpint(tail->value, ==, 0); visit_type_int64(v, NULL, &val, &err); - g_assert_cmpint(val, ==, 1); /* BUG */ + error_free_or_abort(&err); visit_check_list(v, &error_abort); visit_end_list(v, (void **)&res); -- cgit v1.2.3-55-g7522 From 130885938584bb2a4b1a28a44b03752304bba8a2 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 21 Nov 2018 17:44:19 +0100 Subject: test-string-input-visitor: Use virtual walk We now support virtual walks, so use that instead. Reviewed-by: Markus Armbruster Reviewed-by: Eric Blake Signed-off-by: David Hildenbrand Message-Id: <20181121164421.20780-8-david@redhat.com> Signed-off-by: Markus Armbruster --- tests/test-string-input-visitor.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) (limited to 'tests/test-string-input-visitor.c') diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index c5379365a6..718d9a03c3 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -115,7 +115,6 @@ static void test_visitor_in_intList(TestInputVisitorData *data, uint64_t expect5[] = { UINT64_MAX }; Error *err = NULL; int64List *res = NULL; - int64List *tail; Visitor *v; int64_t val; @@ -188,39 +187,28 @@ static void test_visitor_in_intList(TestInputVisitorData *data, v = visitor_input_test_init(data, "0,2-3"); - /* Would be simpler if the visitor genuinely supported virtual walks */ - visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res), - &error_abort); - tail = res; - visit_type_int64(v, NULL, &tail->value, &error_abort); - g_assert_cmpint(tail->value, ==, 0); - tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res)); - g_assert(tail); - visit_type_int64(v, NULL, &tail->value, &error_abort); - g_assert_cmpint(tail->value, ==, 2); - tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res)); - g_assert(tail); + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int64(v, NULL, &val, &error_abort); + g_assert_cmpint(val, ==, 0); + visit_type_int64(v, NULL, &val, &error_abort); + g_assert_cmpint(val, ==, 2); visit_check_list(v, &err); error_free_or_abort(&err); - visit_end_list(v, (void **)&res); - - qapi_free_int64List(res); + visit_end_list(v, NULL); /* Visit beyond end of list */ + v = visitor_input_test_init(data, "0"); - visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res), - &error_abort); - tail = res; - visit_type_int64(v, NULL, &tail->value, &err); - g_assert_cmpint(tail->value, ==, 0); + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int64(v, NULL, &val, &err); + g_assert_cmpint(val, ==, 0); visit_type_int64(v, NULL, &val, &err); error_free_or_abort(&err); - visit_check_list(v, &error_abort); - visit_end_list(v, (void **)&res); - qapi_free_int64List(res); + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); } static void test_visitor_in_bool(TestInputVisitorData *data, -- cgit v1.2.3-55-g7522 From cc871b1cdfc0f9d0520020e38f1bababdcce4141 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 21 Nov 2018 17:44:20 +0100 Subject: test-string-input-visitor: Split off uint64 list tests Basically copy all int64 list tests but adapt them to work on uint64 instead. The values for very big/very small values have to be adapted. Reviewed-by: Markus Armbruster Signed-off-by: David Hildenbrand Message-Id: <20181121164421.20780-9-david@redhat.com> Signed-off-by: Markus Armbruster --- tests/test-string-input-visitor.c | 113 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 4 deletions(-) (limited to 'tests/test-string-input-visitor.c') diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 718d9a03c3..9b1dd44b2d 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -112,7 +112,6 @@ static void test_visitor_in_intList(TestInputVisitorData *data, int64_t expect2[] = { 32767, -32768, -32767 }; int64_t expect3[] = { INT64_MIN, INT64_MAX }; int64_t expect4[] = { 1 }; - uint64_t expect5[] = { UINT64_MAX }; Error *err = NULL; int64List *res = NULL; Visitor *v; @@ -133,9 +132,6 @@ static void test_visitor_in_intList(TestInputVisitorData *data, v = visitor_input_test_init(data, "1-1"); check_ilist(v, expect4, ARRAY_SIZE(expect4)); - v = visitor_input_test_init(data, "18446744073709551615"); - check_ulist(v, expect5, ARRAY_SIZE(expect5)); - /* Value too large */ v = visitor_input_test_init(data, "9223372036854775808"); @@ -211,6 +207,113 @@ static void test_visitor_in_intList(TestInputVisitorData *data, visit_end_list(v, NULL); } +static void test_visitor_in_uintList(TestInputVisitorData *data, + const void *unused) +{ + uint64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7, + 8, 9, 1, 2, 3, 4, 5, 6, 7, 8 }; + uint64_t expect2[] = { 32767, -32768, -32767 }; + uint64_t expect3[] = { INT64_MIN, INT64_MAX }; + uint64_t expect4[] = { 1 }; + uint64_t expect5[] = { UINT64_MAX }; + Error *err = NULL; + uint64List *res = NULL; + Visitor *v; + uint64_t val; + + /* Valid lists */ + + v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8"); + check_ulist(v, expect1, ARRAY_SIZE(expect1)); + + v = visitor_input_test_init(data, "32767,-32768--32767"); + check_ulist(v, expect2, ARRAY_SIZE(expect2)); + + v = visitor_input_test_init(data, + "-9223372036854775808,9223372036854775807"); + check_ulist(v, expect3, ARRAY_SIZE(expect3)); + + v = visitor_input_test_init(data, "1-1"); + check_ulist(v, expect4, ARRAY_SIZE(expect4)); + + v = visitor_input_test_init(data, "18446744073709551615"); + check_ulist(v, expect5, ARRAY_SIZE(expect5)); + + /* Value too large */ + + v = visitor_input_test_init(data, "18446744073709551616"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Value too small */ + + v = visitor_input_test_init(data, "-18446744073709551616"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range not ascending */ + + v = visitor_input_test_init(data, "3-1"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + v = visitor_input_test_init(data, "18446744073709551615-0"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range too big (65536 is the limit against DOS attacks) */ + + v = visitor_input_test_init(data, "0-65536"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Empty list */ + + v = visitor_input_test_init(data, ""); + visit_type_uint64List(v, NULL, &res, &error_abort); + g_assert(!res); + + /* Not a list */ + + v = visitor_input_test_init(data, "not an uint list"); + + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Unvisited list tail */ + + v = visitor_input_test_init(data, "0,2-3"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, NULL, &val, &error_abort); + g_assert_cmpuint(val, ==, 0); + visit_type_uint64(v, NULL, &val, &error_abort); + g_assert_cmpuint(val, ==, 2); + + visit_check_list(v, &err); + error_free_or_abort(&err); + visit_end_list(v, NULL); + + /* Visit beyond end of list */ + + v = visitor_input_test_init(data, "0"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, NULL, &val, &err); + g_assert_cmpuint(val, ==, 0); + visit_type_uint64(v, NULL, &val, &err); + error_free_or_abort(&err); + + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); +} + static void test_visitor_in_bool(TestInputVisitorData *data, const void *unused) { @@ -384,6 +487,8 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_int); input_visitor_test_add("/string-visitor/input/intList", &in_visitor_data, test_visitor_in_intList); + input_visitor_test_add("/string-visitor/input/uintList", + &in_visitor_data, test_visitor_in_uintList); input_visitor_test_add("/string-visitor/input/bool", &in_visitor_data, test_visitor_in_bool); input_visitor_test_add("/string-visitor/input/number", -- cgit v1.2.3-55-g7522 From 345e4010c566d64bfba1592aef03d438f8bbf605 Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Wed, 21 Nov 2018 17:44:21 +0100 Subject: test-string-input-visitor: Add range overflow tests Let's make sure that the range handling code can properly deal with ranges that end at the biggest possible number. Reviewed-by: Markus Armbruster Signed-off-by: David Hildenbrand Message-Id: <20181121164421.20780-10-david@redhat.com> Signed-off-by: Markus Armbruster --- tests/test-string-input-visitor.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tests/test-string-input-visitor.c') diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 9b1dd44b2d..34b54dfc89 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -112,6 +112,7 @@ static void test_visitor_in_intList(TestInputVisitorData *data, int64_t expect2[] = { 32767, -32768, -32767 }; int64_t expect3[] = { INT64_MIN, INT64_MAX }; int64_t expect4[] = { 1 }; + int64_t expect5[] = { INT64_MAX - 2, INT64_MAX - 1, INT64_MAX }; Error *err = NULL; int64List *res = NULL; Visitor *v; @@ -132,6 +133,10 @@ static void test_visitor_in_intList(TestInputVisitorData *data, v = visitor_input_test_init(data, "1-1"); check_ilist(v, expect4, ARRAY_SIZE(expect4)); + v = visitor_input_test_init(data, + "9223372036854775805-9223372036854775807"); + check_ilist(v, expect5, ARRAY_SIZE(expect5)); + /* Value too large */ v = visitor_input_test_init(data, "9223372036854775808"); @@ -216,6 +221,7 @@ static void test_visitor_in_uintList(TestInputVisitorData *data, uint64_t expect3[] = { INT64_MIN, INT64_MAX }; uint64_t expect4[] = { 1 }; uint64_t expect5[] = { UINT64_MAX }; + uint64_t expect6[] = { UINT64_MAX - 2, UINT64_MAX - 1, UINT64_MAX }; Error *err = NULL; uint64List *res = NULL; Visitor *v; @@ -239,6 +245,10 @@ static void test_visitor_in_uintList(TestInputVisitorData *data, v = visitor_input_test_init(data, "18446744073709551615"); check_ulist(v, expect5, ARRAY_SIZE(expect5)); + v = visitor_input_test_init(data, + "18446744073709551613-18446744073709551615"); + check_ulist(v, expect6, ARRAY_SIZE(expect6)); + /* Value too large */ v = visitor_input_test_init(data, "18446744073709551616"); -- cgit v1.2.3-55-g7522