From 430746359fae0ec3e2ac723fd673cb8451b28d7b Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 19 May 2023 10:02:16 -0500 Subject: [PATCH 01/21] iotests: Fix test 104 under NBD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the past, commit a231cb27 ("iotests: Fix 104 for NBD", v2.3.0) added an additional filter to _filter_img_info to rewrite NBD URIs into the expected output form. This recently broke when we tweaked tests to run in a per-format directory, which did not match the regex, because _img_info itself is now already changing SOCK_DIR=/tmp/tmpphjfbphd/raw-nbd-104 into /tmp/tmpphjfbphd/IMGFMT-nbd-104 prior to _img_info_filter getting a chance to further filter things. While diagnosing the problem, I also noticed some filter lines rendered completely useless by a typo when we switched from TCP to Unix sockets for NBD (in shell, '\\+' is different from "\\+" (one gives two backslash to the regex, matching the literal 2-byte sequence <\+> after a single digit; the other gives one backslash to the regex, as the metacharacter \+ to match one or more of <[0-9]>); since the literal string is not a valid URI, that regex hasn't been matching anything for years so it is fine to just drop it rather than fix the typo. Fixes: f3923a72 ("iotests: Switch nbd tests to use Unix rather than TCP", v4.2.0) Fixes: 5ba7db09 ("iotests: always use a unique sub-directory per test", v8.0.0) Signed-off-by: Eric Blake Message-Id: <20230519150216.2599189-1-eblake@redhat.com> Reviewed-by: Daniel P. Berrangé --- tests/qemu-iotests/common.filter | 4 +--- tests/qemu-iotests/common.rc | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 6b32c7fbfa..fc3c64bcb8 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright Red Hat # Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or @@ -131,7 +131,6 @@ _filter_img_create_filenames() -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e 's#SOCK_DIR/fuse-#TEST_DIR/#g' \ -e "s#$IMGFMT#IMGFMT#g" \ - -e 's#nbd:127.0.0.1:[0-9]\\+#TEST_DIR/t.IMGFMT#g' \ -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' } @@ -229,7 +228,6 @@ _filter_img_info() -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR#SOCK_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ - -e 's#nbd://127.0.0.1:[0-9]\\+$#TEST_DIR/t.IMGFMT#g' \ -e 's#nbd+unix:///\??socket=SOCK_DIR/nbd#TEST_DIR/t.IMGFMT#g' \ -e 's#SOCK_DIR/fuse-#TEST_DIR/#g' \ -e "/encrypted: yes/d" \ diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index f4476b62f7..d145f08201 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (C) 2009 Red Hat, Inc. +# Copyright Red Hat # Copyright (c) 2000-2006 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify @@ -717,6 +717,7 @@ _img_info() -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$SOCK_DIR/fuse-#TEST_DIR/#g" \ + -e "s#$SOCK_DIR/#SOCK_DIR/#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \ -e "/^disk size:/ D" \ From 5cf899e21555d57b9ee1d0a8256f9a2230e88658 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 13:46:31 -0500 Subject: [PATCH 02/21] qcow2: Explicit mention of padding bytes Although we already covered the need for padding bytes with our changes in commit 3ae3fcfa, commit 66fcbca5 (both v5.0.0) added one byte and relied on the rest of the text for implicitly covering 7 padding bytes. For consistency with other parts of the header (such as the header extension format listing padding from n - m, or the snapshot table entry listing variable padding), we might as well call out the remaining 7 bytes as padding until such time (as any) as they gain another meaning. Signed-off-by: Eric Blake CC: Vladimir Sementsov-Ogievskiy Message-Id: <20230522184631.47211-1-eblake@redhat.com> Reviewed-by: Vladimir Sementsov-Ogievskiy --- docs/interop/qcow2.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index e7f036c286..2c4618375a 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -226,6 +226,7 @@ version 2. in QEMU. However, clusters with the deflate compression type do not have zlib headers. + 105 - 111: Padding, contents defined below. === Header padding === From 3a5925922520f934f7b0f96d0c65a041540d7738 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 11 May 2023 21:10:15 -0500 Subject: [PATCH 03/21] test-cutils: Avoid g_assert in unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit glib documentation[1] is clear: g_assert() should be avoided in unit tests because it is ineffective if G_DISABLE_ASSERT is defined; unit tests should stick to constructs based on g_assert_true() instead. Note that since commit 262a69f428, we intentionally state that you cannot define G_DISABLE_ASSERT while building qemu; but our code can be copied to other projects without that restriction, so we should be consistent. For most of the replacements in this patch, using g_assert_cmpstr() would be a regression in quality - although it would helpfully display the string contents of both pointers on test failure, here, we really do care about pointer equality, not just string content equality. But when a NULL pointer is expected, g_assert_null works fine. [1] https://libsoup.org/glib/glib-Testing.html#g-assert Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230522190441.64278-2-eblake@redhat.com> --- tests/unit/test-cutils.c | 324 +++++++++++++++++++-------------------- 1 file changed, 162 insertions(+), 162 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 3c4f875420..0202ac0d5b 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -1,7 +1,7 @@ /* * cutils.c unit-tests * - * Copyright (C) 2013 Red Hat Inc. + * Copyright Red Hat * * Authors: * Eduardo Habkost @@ -40,7 +40,7 @@ static void test_parse_uint_null(void) g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpint(i, ==, 0); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_parse_uint_empty(void) @@ -55,7 +55,7 @@ static void test_parse_uint_empty(void) g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_parse_uint_whitespace(void) @@ -70,7 +70,7 @@ static void test_parse_uint_whitespace(void) g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_true(endptr == str); } @@ -86,7 +86,7 @@ static void test_parse_uint_invalid(void) g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpint(i, ==, 0); - g_assert(endptr == str); + g_assert_true(endptr == str); } @@ -102,7 +102,7 @@ static void test_parse_uint_trailing(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_parse_uint_correct(void) @@ -117,7 +117,7 @@ static void test_parse_uint_correct(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_octal(void) @@ -132,7 +132,7 @@ static void test_parse_uint_octal(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_decimal(void) @@ -147,7 +147,7 @@ static void test_parse_uint_decimal(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } @@ -163,7 +163,7 @@ static void test_parse_uint_llong_max(void) g_assert_cmpint(r, ==, 0); g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -180,7 +180,7 @@ static void test_parse_uint_overflow(void) g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpint(i, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_negative(void) @@ -195,7 +195,7 @@ static void test_parse_uint_negative(void) g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpint(i, ==, 0); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } @@ -235,7 +235,7 @@ static void test_qemu_strtoi_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoi_null(void) @@ -248,7 +248,7 @@ static void test_qemu_strtoi_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoi_empty(void) @@ -262,7 +262,7 @@ static void test_qemu_strtoi_empty(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi_whitespace(void) @@ -276,7 +276,7 @@ static void test_qemu_strtoi_whitespace(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi_invalid(void) @@ -290,7 +290,7 @@ static void test_qemu_strtoi_invalid(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi_trailing(void) @@ -305,7 +305,7 @@ static void test_qemu_strtoi_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoi_octal(void) @@ -320,7 +320,7 @@ static void test_qemu_strtoi_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -328,7 +328,7 @@ static void test_qemu_strtoi_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_decimal(void) @@ -343,7 +343,7 @@ static void test_qemu_strtoi_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -352,7 +352,7 @@ static void test_qemu_strtoi_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_hex(void) @@ -367,7 +367,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -376,7 +376,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -385,7 +385,7 @@ static void test_qemu_strtoi_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoi_max(void) @@ -400,7 +400,7 @@ static void test_qemu_strtoi_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, INT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -416,7 +416,7 @@ static void test_qemu_strtoi_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -432,7 +432,7 @@ static void test_qemu_strtoi_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MIN); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -448,7 +448,7 @@ static void test_qemu_strtoi_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_full_correct(void) @@ -473,7 +473,7 @@ static void test_qemu_strtoi_full_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoi_full_empty(void) @@ -535,7 +535,7 @@ static void test_qemu_strtoui_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoui_null(void) @@ -548,7 +548,7 @@ static void test_qemu_strtoui_null(void) err = qemu_strtoui(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoui_empty(void) @@ -562,7 +562,7 @@ static void test_qemu_strtoui_empty(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoui_whitespace(void) @@ -576,7 +576,7 @@ static void test_qemu_strtoui_whitespace(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoui_invalid(void) @@ -590,7 +590,7 @@ static void test_qemu_strtoui_invalid(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoui_trailing(void) @@ -605,7 +605,7 @@ static void test_qemu_strtoui_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoui_octal(void) @@ -620,7 +620,7 @@ static void test_qemu_strtoui_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -628,7 +628,7 @@ static void test_qemu_strtoui_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_decimal(void) @@ -643,7 +643,7 @@ static void test_qemu_strtoui_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -652,7 +652,7 @@ static void test_qemu_strtoui_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_hex(void) @@ -667,7 +667,7 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -676,7 +676,7 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -685,7 +685,7 @@ static void test_qemu_strtoui_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoui_max(void) @@ -700,7 +700,7 @@ static void test_qemu_strtoui_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, UINT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -716,7 +716,7 @@ static void test_qemu_strtoui_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, UINT_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -732,7 +732,7 @@ static void test_qemu_strtoui_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpuint(res, ==, (unsigned int)-1); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -748,7 +748,7 @@ static void test_qemu_strtoui_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, (unsigned int)-321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoui_full_correct(void) @@ -830,7 +830,7 @@ static void test_qemu_strtol_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtol_null(void) @@ -843,7 +843,7 @@ static void test_qemu_strtol_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtol_empty(void) @@ -857,7 +857,7 @@ static void test_qemu_strtol_empty(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtol_whitespace(void) @@ -871,7 +871,7 @@ static void test_qemu_strtol_whitespace(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtol_invalid(void) @@ -885,7 +885,7 @@ static void test_qemu_strtol_invalid(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtol_trailing(void) @@ -900,7 +900,7 @@ static void test_qemu_strtol_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtol_octal(void) @@ -915,7 +915,7 @@ static void test_qemu_strtol_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -923,7 +923,7 @@ static void test_qemu_strtol_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_decimal(void) @@ -938,7 +938,7 @@ static void test_qemu_strtol_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -947,7 +947,7 @@ static void test_qemu_strtol_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_hex(void) @@ -962,7 +962,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -971,7 +971,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -980,7 +980,7 @@ static void test_qemu_strtol_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtol_max(void) @@ -995,7 +995,7 @@ static void test_qemu_strtol_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, LONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -1011,7 +1011,7 @@ static void test_qemu_strtol_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_underflow(void) @@ -1026,7 +1026,7 @@ static void test_qemu_strtol_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MIN); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_negative(void) @@ -1041,7 +1041,7 @@ static void test_qemu_strtol_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtol_full_correct(void) @@ -1066,7 +1066,7 @@ static void test_qemu_strtol_full_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtol_full_empty(void) @@ -1128,7 +1128,7 @@ static void test_qemu_strtoul_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoul_null(void) @@ -1141,7 +1141,7 @@ static void test_qemu_strtoul_null(void) err = qemu_strtoul(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoul_empty(void) @@ -1155,7 +1155,7 @@ static void test_qemu_strtoul_empty(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoul_whitespace(void) @@ -1169,7 +1169,7 @@ static void test_qemu_strtoul_whitespace(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoul_invalid(void) @@ -1183,7 +1183,7 @@ static void test_qemu_strtoul_invalid(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoul_trailing(void) @@ -1198,7 +1198,7 @@ static void test_qemu_strtoul_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoul_octal(void) @@ -1213,7 +1213,7 @@ static void test_qemu_strtoul_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); res = 999; endptr = &f; @@ -1221,7 +1221,7 @@ static void test_qemu_strtoul_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_decimal(void) @@ -1236,7 +1236,7 @@ static void test_qemu_strtoul_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; res = 999; @@ -1245,7 +1245,7 @@ static void test_qemu_strtoul_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_hex(void) @@ -1260,7 +1260,7 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; res = 999; @@ -1269,7 +1269,7 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; res = 999; @@ -1278,7 +1278,7 @@ static void test_qemu_strtoul_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoul_max(void) @@ -1293,7 +1293,7 @@ static void test_qemu_strtoul_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, ULONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -1309,7 +1309,7 @@ static void test_qemu_strtoul_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, ULONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_underflow(void) @@ -1324,7 +1324,7 @@ static void test_qemu_strtoul_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpuint(res, ==, -1ul); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_negative(void) @@ -1339,7 +1339,7 @@ static void test_qemu_strtoul_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, -321ul); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_full_correct(void) @@ -1421,7 +1421,7 @@ static void test_qemu_strtoi64_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtoi64_null(void) @@ -1434,7 +1434,7 @@ static void test_qemu_strtoi64_null(void) err = qemu_strtoi64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtoi64_empty(void) @@ -1448,7 +1448,7 @@ static void test_qemu_strtoi64_empty(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_whitespace(void) @@ -1462,7 +1462,7 @@ static void test_qemu_strtoi64_whitespace(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_invalid(void) @@ -1476,7 +1476,7 @@ static void test_qemu_strtoi64_invalid(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtoi64_trailing(void) @@ -1491,7 +1491,7 @@ static void test_qemu_strtoi64_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtoi64_octal(void) @@ -1506,7 +1506,7 @@ static void test_qemu_strtoi64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); endptr = &f; res = 999; @@ -1514,7 +1514,7 @@ static void test_qemu_strtoi64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_decimal(void) @@ -1529,7 +1529,7 @@ static void test_qemu_strtoi64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; endptr = &f; @@ -1538,7 +1538,7 @@ static void test_qemu_strtoi64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_hex(void) @@ -1553,7 +1553,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; endptr = &f; @@ -1562,7 +1562,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; endptr = &f; @@ -1571,7 +1571,7 @@ static void test_qemu_strtoi64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtoi64_max(void) @@ -1586,7 +1586,7 @@ static void test_qemu_strtoi64_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, LLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -1602,7 +1602,7 @@ static void test_qemu_strtoi64_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_underflow(void) @@ -1617,7 +1617,7 @@ static void test_qemu_strtoi64_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LLONG_MIN); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_negative(void) @@ -1632,7 +1632,7 @@ static void test_qemu_strtoi64_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi64_full_correct(void) @@ -1717,7 +1717,7 @@ static void test_qemu_strtou64_correct(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 12345); - g_assert(endptr == str + 5); + g_assert_true(endptr == str + 5); } static void test_qemu_strtou64_null(void) @@ -1730,7 +1730,7 @@ static void test_qemu_strtou64_null(void) err = qemu_strtou64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == NULL); + g_assert_null(endptr); } static void test_qemu_strtou64_empty(void) @@ -1744,7 +1744,7 @@ static void test_qemu_strtou64_empty(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtou64_whitespace(void) @@ -1758,7 +1758,7 @@ static void test_qemu_strtou64_whitespace(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtou64_invalid(void) @@ -1772,7 +1772,7 @@ static void test_qemu_strtou64_invalid(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtou64_trailing(void) @@ -1787,7 +1787,7 @@ static void test_qemu_strtou64_trailing(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtou64_octal(void) @@ -1802,7 +1802,7 @@ static void test_qemu_strtou64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); endptr = &f; res = 999; @@ -1810,7 +1810,7 @@ static void test_qemu_strtou64_octal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 0123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_decimal(void) @@ -1825,7 +1825,7 @@ static void test_qemu_strtou64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "123"; endptr = &f; @@ -1834,7 +1834,7 @@ static void test_qemu_strtou64_decimal(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, 123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_hex(void) @@ -1849,7 +1849,7 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x123"; endptr = &f; @@ -1858,7 +1858,7 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0x123); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); str = "0x"; endptr = &f; @@ -1867,7 +1867,7 @@ static void test_qemu_strtou64_hex(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); } static void test_qemu_strtou64_max(void) @@ -1882,7 +1882,7 @@ static void test_qemu_strtou64_max(void) g_assert_cmpint(err, ==, 0); g_assert_cmphex(res, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); g_free(str); } @@ -1898,7 +1898,7 @@ static void test_qemu_strtou64_overflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, ULLONG_MAX); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_underflow(void) @@ -1913,7 +1913,7 @@ static void test_qemu_strtou64_underflow(void) g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, -1ull); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_negative(void) @@ -1928,7 +1928,7 @@ static void test_qemu_strtou64_negative(void) g_assert_cmpint(err, ==, 0); g_assert_cmpuint(res, ==, -321ull); - g_assert(endptr == str + strlen(str)); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_full_correct(void) @@ -2013,7 +2013,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); /* Leading 0 gives decimal results, not octal */ str = "08"; @@ -2022,7 +2022,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 8); - g_assert(endptr == str + 2); + g_assert_true(endptr == str + 2); /* Leading space is ignored */ str = " 12345"; @@ -2031,7 +2031,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); - g_assert(endptr == str + 6); + g_assert_true(endptr == str + 6); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2044,7 +2044,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x1fffffffffffff); - g_assert(endptr == str + 16); + g_assert_true(endptr == str + 16); str = "9007199254740992"; /* 2^53 */ endptr = str; @@ -2052,7 +2052,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x20000000000000); - g_assert(endptr == str + 16); + g_assert_true(endptr == str + 16); str = "9007199254740993"; /* 2^53+1 */ endptr = str; @@ -2060,7 +2060,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x20000000000001); - g_assert(endptr == str + 16); + g_assert_true(endptr == str + 16); str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ endptr = str; @@ -2068,7 +2068,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0xfffffffffffff800); - g_assert(endptr == str + 20); + g_assert_true(endptr == str + 20); str = "18446744073709550591"; /* 0xfffffffffffffbff */ endptr = str; @@ -2076,7 +2076,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0xfffffffffffffbff); - g_assert(endptr == str + 20); + g_assert_true(endptr == str + 20); str = "18446744073709551615"; /* 0xffffffffffffffff */ endptr = str; @@ -2084,7 +2084,7 @@ static void test_qemu_strtosz_simple(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0xffffffffffffffff); - g_assert(endptr == str + 20); + g_assert_true(endptr == str + 20); } static void test_qemu_strtosz_hex(void) @@ -2100,7 +2100,7 @@ static void test_qemu_strtosz_hex(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); str = "0xab"; endptr = str; @@ -2108,7 +2108,7 @@ static void test_qemu_strtosz_hex(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 171); - g_assert(endptr == str + 4); + g_assert_true(endptr == str + 4); str = "0xae"; endptr = str; @@ -2116,7 +2116,7 @@ static void test_qemu_strtosz_hex(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 174); - g_assert(endptr == str + 4); + g_assert_true(endptr == str + 4); } static void test_qemu_strtosz_units(void) @@ -2139,56 +2139,56 @@ static void test_qemu_strtosz_units(void) err = qemu_strtosz_MiB(none, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, MiB); - g_assert(endptr == none + 1); + g_assert_true(endptr == none + 1); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(b, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1); - g_assert(endptr == b + 2); + g_assert_true(endptr == b + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(k, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, KiB); - g_assert(endptr == k + 2); + g_assert_true(endptr == k + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(m, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, MiB); - g_assert(endptr == m + 2); + g_assert_true(endptr == m + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(g, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, GiB); - g_assert(endptr == g + 2); + g_assert_true(endptr == g + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(t, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, TiB); - g_assert(endptr == t + 2); + g_assert_true(endptr == t + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(p, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, PiB); - g_assert(endptr == p + 2); + g_assert_true(endptr == p + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(e, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, EiB); - g_assert(endptr == e + 2); + g_assert_true(endptr == e + 2); } static void test_qemu_strtosz_float(void) @@ -2204,7 +2204,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, EiB / 2); - g_assert(endptr == str + 4); + g_assert_true(endptr == str + 4); /* For convenience, a fraction of 0 is tolerated even on bytes */ str = "1.0B"; @@ -2213,7 +2213,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1); - g_assert(endptr == str + 4); + g_assert_true(endptr == str + 4); /* An empty fraction is tolerated */ str = "1.k"; @@ -2222,7 +2222,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1024); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); /* For convenience, we permit values that are not byte-exact */ str = "12.345M"; @@ -2231,7 +2231,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); - g_assert(endptr == str + 7); + g_assert_true(endptr == str + 7); } static void test_qemu_strtosz_invalid(void) @@ -2246,35 +2246,35 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = " \t "; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "crap"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "inf"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "NaN"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* Fractional values require scale larger than bytes */ str = "1.1B"; @@ -2282,14 +2282,14 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "1.1"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* No floating point exponents */ str = "1.5e1k"; @@ -2297,14 +2297,14 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "1.5E+0k"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* No hex fractions */ str = "0x1.8k"; @@ -2312,7 +2312,7 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* No suffixes */ str = "0x18M"; @@ -2320,7 +2320,7 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); /* No negative values */ str = "-0"; @@ -2328,14 +2328,14 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); str = "-1"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str); + g_assert_true(endptr == str); } static void test_qemu_strtosz_trailing(void) @@ -2351,7 +2351,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz_MiB(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123 * MiB); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2364,7 +2364,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1024); - g_assert(endptr == str + 2); + g_assert_true(endptr == str + 2); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2377,7 +2377,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 1); + g_assert_true(endptr == str + 1); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2390,7 +2390,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0); - g_assert(endptr == str + 2); + g_assert_true(endptr == str + 2); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2403,7 +2403,7 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); @@ -2423,14 +2423,14 @@ static void test_qemu_strtosz_erange(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str + 20); + g_assert_true(endptr == str + 20); str = "20E"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, 0xbaadf00d); - g_assert(endptr == str + 3); + g_assert_true(endptr == str + 3); } static void test_qemu_strtosz_metric(void) @@ -2446,7 +2446,7 @@ static void test_qemu_strtosz_metric(void) err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345000); - g_assert(endptr == str + 6); + g_assert_true(endptr == str + 6); str = "12.345M"; endptr = str; @@ -2454,7 +2454,7 @@ static void test_qemu_strtosz_metric(void) err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345000); - g_assert(endptr == str + 7); + g_assert_true(endptr == str + 7); } static void test_freq_to_str(void) From 3b4790d4ec1f01a1d0575bda23aa08a2061d601c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Thu, 11 May 2023 21:10:16 -0500 Subject: [PATCH 04/21] test-cutils: Use g_assert_cmpuint where appropriate When debugging test failures, seeing unsigned values as large positive values rather than negative values matters (assuming glib 2.78+; given that I just fixed a bug in glib 2.76 [1] where g_assert_cmpuint displays signed instead of unsigned values). No impact when the test is passing, but using a consistent style will matter more in upcoming test additions. Also, some tests are better with cmphex. While at it, fix some spacing and minor typing issues spotted nearby. [1] https://gitlab.gnome.org/GNOME/glib/-/issues/2997 Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-3-eblake@redhat.com> --- tests/unit/test-cutils.c | 148 +++++++++++++++++++-------------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 0202ac0d5b..38bd399020 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -39,7 +39,7 @@ static void test_parse_uint_null(void) r = parse_uint(NULL, &i, &endptr, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_null(endptr); } @@ -54,7 +54,7 @@ static void test_parse_uint_empty(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str); } @@ -69,7 +69,7 @@ static void test_parse_uint_whitespace(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str); } @@ -85,7 +85,7 @@ static void test_parse_uint_invalid(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str); } @@ -101,7 +101,7 @@ static void test_parse_uint_trailing(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); g_assert_true(endptr == str + 3); } @@ -116,7 +116,7 @@ static void test_parse_uint_correct(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); g_assert_true(endptr == str + strlen(str)); } @@ -131,7 +131,7 @@ static void test_parse_uint_octal(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 0123); + g_assert_cmpuint(i, ==, 0123); g_assert_true(endptr == str + strlen(str)); } @@ -146,7 +146,7 @@ static void test_parse_uint_decimal(void) r = parse_uint(str, &i, &endptr, 10); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); g_assert_true(endptr == str + strlen(str)); } @@ -162,7 +162,7 @@ static void test_parse_uint_llong_max(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, (unsigned long long)LLONG_MAX + 1); + g_assert_cmpuint(i, ==, (unsigned long long)LLONG_MAX + 1); g_assert_true(endptr == str + strlen(str)); g_free(str); @@ -179,7 +179,7 @@ static void test_parse_uint_overflow(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -ERANGE); - g_assert_cmpint(i, ==, ULLONG_MAX); + g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); } @@ -194,7 +194,7 @@ static void test_parse_uint_negative(void) r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -ERANGE); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str + strlen(str)); } @@ -208,7 +208,7 @@ static void test_parse_uint_full_trailing(void) r = parse_uint_full(str, &i, 0); g_assert_cmpint(r, ==, -EINVAL); - g_assert_cmpint(i, ==, 0); + g_assert_cmpuint(i, ==, 0); } static void test_parse_uint_full_correct(void) @@ -220,7 +220,7 @@ static void test_parse_uint_full_correct(void) r = parse_uint_full(str, &i, 0); g_assert_cmpint(r, ==, 0); - g_assert_cmpint(i, ==, 123); + g_assert_cmpuint(i, ==, 123); } static void test_qemu_strtoi_correct(void) @@ -428,7 +428,7 @@ static void test_qemu_strtoi_underflow(void) int res = 999; int err; - err = qemu_strtoi(str, &endptr, 0, &res); + err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MIN); @@ -479,10 +479,10 @@ static void test_qemu_strtoi_full_null(void) static void test_qemu_strtoi_full_empty(void) { const char *str = ""; - int res = 999L; + int res = 999; int err; - err = qemu_strtoi(str, NULL, 0, &res); + err = qemu_strtoi(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } @@ -728,7 +728,7 @@ static void test_qemu_strtoui_underflow(void) unsigned int res = 999; int err; - err = qemu_strtoui(str, &endptr, 0, &res); + err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpuint(res, ==, (unsigned int)-1); @@ -1022,7 +1022,7 @@ static void test_qemu_strtol_underflow(void) long res = 999; int err; - err = qemu_strtol(str, &endptr, 0, &res); + err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MIN); @@ -1075,7 +1075,7 @@ static void test_qemu_strtol_full_empty(void) long res = 999L; int err; - err = qemu_strtol(str, NULL, 0, &res); + err = qemu_strtol(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } @@ -1320,7 +1320,7 @@ static void test_qemu_strtoul_underflow(void) unsigned long res = 999; int err; - err = qemu_strtoul(str, &endptr, 0, &res); + err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpuint(res, ==, -1ul); @@ -1613,7 +1613,7 @@ static void test_qemu_strtoi64_underflow(void) int64_t res = 999; int err; - err = qemu_strtoi64(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LLONG_MIN); @@ -1909,7 +1909,7 @@ static void test_qemu_strtou64_underflow(void) uint64_t res = 999; int err; - err = qemu_strtou64(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmphex(res, ==, -1ull); @@ -2012,7 +2012,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str + 1); /* Leading 0 gives decimal results, not octal */ @@ -2021,7 +2021,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 8); + g_assert_cmpuint(res, ==, 8); g_assert_true(endptr == str + 2); /* Leading space is ignored */ @@ -2030,20 +2030,20 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); + g_assert_cmpuint(res, ==, 12345); g_assert_true(endptr == str + 6); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); + g_assert_cmpuint(res, ==, 12345); str = "9007199254740991"; /* 2^53-1 */ endptr = str; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x1fffffffffffff); + g_assert_cmphex(res, ==, 0x1fffffffffffffULL); g_assert_true(endptr == str + 16); str = "9007199254740992"; /* 2^53 */ @@ -2051,7 +2051,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000000); + g_assert_cmphex(res, ==, 0x20000000000000ULL); g_assert_true(endptr == str + 16); str = "9007199254740993"; /* 2^53+1 */ @@ -2059,7 +2059,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x20000000000001); + g_assert_cmphex(res, ==, 0x20000000000001ULL); g_assert_true(endptr == str + 16); str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ @@ -2067,7 +2067,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffff800); + g_assert_cmphex(res, ==, 0xfffffffffffff800ULL); g_assert_true(endptr == str + 20); str = "18446744073709550591"; /* 0xfffffffffffffbff */ @@ -2075,7 +2075,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xfffffffffffffbff); + g_assert_cmphex(res, ==, 0xfffffffffffffbffULL); g_assert_true(endptr == str + 20); str = "18446744073709551615"; /* 0xffffffffffffffff */ @@ -2083,7 +2083,7 @@ static void test_qemu_strtosz_simple(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0xffffffffffffffff); + g_assert_cmphex(res, ==, 0xffffffffffffffffULL); g_assert_true(endptr == str + 20); } @@ -2099,7 +2099,7 @@ static void test_qemu_strtosz_hex(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str + 3); str = "0xab"; @@ -2107,7 +2107,7 @@ static void test_qemu_strtosz_hex(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 171); + g_assert_cmpuint(res, ==, 171); g_assert_true(endptr == str + 4); str = "0xae"; @@ -2115,7 +2115,7 @@ static void test_qemu_strtosz_hex(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 174); + g_assert_cmpuint(res, ==, 174); g_assert_true(endptr == str + 4); } @@ -2138,56 +2138,56 @@ static void test_qemu_strtosz_units(void) res = 0xbaadf00d; err = qemu_strtosz_MiB(none, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, MiB); + g_assert_cmpuint(res, ==, MiB); g_assert_true(endptr == none + 1); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(b, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1); + g_assert_cmpuint(res, ==, 1); g_assert_true(endptr == b + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(k, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, KiB); + g_assert_cmpuint(res, ==, KiB); g_assert_true(endptr == k + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(m, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, MiB); + g_assert_cmpuint(res, ==, MiB); g_assert_true(endptr == m + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(g, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, GiB); + g_assert_cmpuint(res, ==, GiB); g_assert_true(endptr == g + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(t, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, TiB); + g_assert_cmpuint(res, ==, TiB); g_assert_true(endptr == t + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(p, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, PiB); + g_assert_cmpuint(res, ==, PiB); g_assert_true(endptr == p + 2); endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(e, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, EiB); + g_assert_cmpuint(res, ==, EiB); g_assert_true(endptr == e + 2); } @@ -2203,7 +2203,7 @@ static void test_qemu_strtosz_float(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, EiB / 2); + g_assert_cmpuint(res, ==, EiB / 2); g_assert_true(endptr == str + 4); /* For convenience, a fraction of 0 is tolerated even on bytes */ @@ -2212,7 +2212,7 @@ static void test_qemu_strtosz_float(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1); + g_assert_cmpuint(res, ==, 1); g_assert_true(endptr == str + 4); /* An empty fraction is tolerated */ @@ -2221,7 +2221,7 @@ static void test_qemu_strtosz_float(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1024); + g_assert_cmpuint(res, ==, 1024); g_assert_true(endptr == str + 3); /* For convenience, we permit values that are not byte-exact */ @@ -2230,7 +2230,7 @@ static void test_qemu_strtosz_float(void) res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); + g_assert_cmpuint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); g_assert_true(endptr == str + 7); } @@ -2245,35 +2245,35 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = " \t "; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "crap"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "inf"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "NaN"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* Fractional values require scale larger than bytes */ @@ -2281,14 +2281,14 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "1.1"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* No floating point exponents */ @@ -2296,14 +2296,14 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "1.5E+0k"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* No hex fractions */ @@ -2311,7 +2311,7 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* No suffixes */ @@ -2319,7 +2319,7 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); /* No negative values */ @@ -2327,14 +2327,14 @@ static void test_qemu_strtosz_invalid(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); str = "-1"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); } @@ -2350,65 +2350,65 @@ static void test_qemu_strtosz_trailing(void) res = 0xbaadf00d; err = qemu_strtosz_MiB(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123 * MiB); + g_assert_cmpuint(res, ==, 123 * MiB); g_assert_true(endptr == str + 3); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); str = "1kiB"; endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 1024); + g_assert_cmpuint(res, ==, 1024); g_assert_true(endptr == str + 2); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); str = "0x"; endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str + 1); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); str = "0.NaN"; endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str + 2); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); str = "123-45"; endptr = NULL; res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); g_assert_true(endptr == str + 3); res = 0xbaadf00d; err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); } static void test_qemu_strtosz_erange(void) @@ -2422,14 +2422,14 @@ static void test_qemu_strtosz_erange(void) endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str + 20); str = "20E"; endptr = NULL; err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, 0xbaadf00d); + g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str + 3); } @@ -2445,7 +2445,7 @@ static void test_qemu_strtosz_metric(void) res = 0xbaadf00d; err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345000); + g_assert_cmpuint(res, ==, 12345000); g_assert_true(endptr == str + 6); str = "12.345M"; @@ -2453,7 +2453,7 @@ static void test_qemu_strtosz_metric(void) res = 0xbaadf00d; err = qemu_strtosz_metric(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345000); + g_assert_cmpuint(res, ==, 12345000); g_assert_true(endptr == str + 7); } From d326d03bcdf0751049ec0bfbe246a2e828008a13 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:25 -0500 Subject: [PATCH 05/21] test-cutils: Test integral qemu_strto* value on failures We are inconsistent on the contents of *value after a strto* parse failure. I found the following behaviors: - parse_uint() and parse_uint_full(), which document that *value is slammed to 0 on all EINVAL failures and 0 or UINT_MAX on ERANGE failures, and has unit tests for that (note that parse_uint requires non-NULL endptr, and does not fail with EINVAL for trailing junk) - qemu_strtosz(), which leaves *value untouched on all failures (both EINVAL and ERANGE), and has unit tests but not documentation for that - qemu_strtoi() and other integral friends, which document *value on ERANGE failures but is unspecified on EINVAL (other than implicitly by comparison to libc strto*); there, *value is untouched for NULL string, slammed to 0 on no conversion, and left at the prefix value on NULL endptr; unit tests do not consistently check the value - qemu_strtod(), which documents *value on ERANGE failures but is unspecified on EINVAL; there, *value is untouched for NULL string, slammed to 0.0 for no conversion, and left at the prefix value on NULL endptr; there are no unit tests (other than indirectly through qemu_strtosz) - qemu_strtod_finite(), which documents *value on ERANGE failures but is unspecified on EINVAL; there, *value is left at the prefix for 'inf' or 'nan' and untouched in all other cases; there are no unit tests (other than indirectly through qemu_strtosz) Upcoming patches will change behaviors for consistency, but it's best to first have more unit test coverage to see the impact of those changes. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-4-eblake@redhat.com> --- tests/unit/test-cutils.c | 58 +++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 38bd399020..1eeaf21ae2 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -248,6 +248,7 @@ static void test_qemu_strtoi_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -262,6 +263,7 @@ static void test_qemu_strtoi_empty(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -276,6 +278,7 @@ static void test_qemu_strtoi_whitespace(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -290,6 +293,7 @@ static void test_qemu_strtoi_invalid(void) err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -473,6 +477,7 @@ static void test_qemu_strtoi_full_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -485,6 +490,7 @@ static void test_qemu_strtoi_full_empty(void) err = qemu_strtoi(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi_full_negative(void) @@ -502,18 +508,19 @@ static void test_qemu_strtoi_full_negative(void) static void test_qemu_strtoi_full_trailing(void) { const char *str = "123xxx"; - int res; + int res = 999; int err; err = qemu_strtoi(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtoi_full_max(void) { char *str = g_strdup_printf("%d", INT_MAX); - int res; + int res = 999; int err; err = qemu_strtoi(str, NULL, 0, &res); @@ -548,6 +555,7 @@ static void test_qemu_strtoui_null(void) err = qemu_strtoui(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); g_assert_null(endptr); } @@ -562,6 +570,7 @@ static void test_qemu_strtoui_empty(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -576,6 +585,7 @@ static void test_qemu_strtoui_whitespace(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -590,6 +600,7 @@ static void test_qemu_strtoui_invalid(void) err = qemu_strtoui(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -771,6 +782,7 @@ static void test_qemu_strtoui_full_null(void) err = qemu_strtoui(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); } static void test_qemu_strtoui_full_empty(void) @@ -782,7 +794,9 @@ static void test_qemu_strtoui_full_empty(void) err = qemu_strtoui(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } + static void test_qemu_strtoui_full_negative(void) { const char *str = " \t -321"; @@ -797,12 +811,13 @@ static void test_qemu_strtoui_full_negative(void) static void test_qemu_strtoui_full_trailing(void) { const char *str = "123xxx"; - unsigned int res; + unsigned int res = 999; int err; err = qemu_strtoui(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 123); } static void test_qemu_strtoui_full_max(void) @@ -843,6 +858,7 @@ static void test_qemu_strtol_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -857,6 +873,7 @@ static void test_qemu_strtol_empty(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -871,6 +888,7 @@ static void test_qemu_strtol_whitespace(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -885,6 +903,7 @@ static void test_qemu_strtol_invalid(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -1066,6 +1085,7 @@ static void test_qemu_strtol_full_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -1078,6 +1098,7 @@ static void test_qemu_strtol_full_empty(void) err = qemu_strtol(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtol_full_negative(void) @@ -1095,18 +1116,19 @@ static void test_qemu_strtol_full_negative(void) static void test_qemu_strtol_full_trailing(void) { const char *str = "123xxx"; - long res; + long res = 999; int err; err = qemu_strtol(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtol_full_max(void) { char *str = g_strdup_printf("%ld", LONG_MAX); - long res; + long res = 999; int err; err = qemu_strtol(str, NULL, 0, &res); @@ -1141,6 +1163,7 @@ static void test_qemu_strtoul_null(void) err = qemu_strtoul(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); g_assert_null(endptr); } @@ -1155,6 +1178,7 @@ static void test_qemu_strtoul_empty(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1169,6 +1193,7 @@ static void test_qemu_strtoul_whitespace(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1183,6 +1208,7 @@ static void test_qemu_strtoul_invalid(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1362,6 +1388,7 @@ static void test_qemu_strtoul_full_null(void) err = qemu_strtoul(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); } static void test_qemu_strtoul_full_empty(void) @@ -1373,7 +1400,9 @@ static void test_qemu_strtoul_full_empty(void) err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } + static void test_qemu_strtoul_full_negative(void) { const char *str = " \t -321"; @@ -1388,12 +1417,13 @@ static void test_qemu_strtoul_full_negative(void) static void test_qemu_strtoul_full_trailing(void) { const char *str = "123xxx"; - unsigned long res; + unsigned long res = 999; int err; err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 123); } static void test_qemu_strtoul_full_max(void) @@ -1434,6 +1464,7 @@ static void test_qemu_strtoi64_null(void) err = qemu_strtoi64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); g_assert_null(endptr); } @@ -1448,6 +1479,7 @@ static void test_qemu_strtoi64_empty(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -1462,6 +1494,7 @@ static void test_qemu_strtoi64_whitespace(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -1476,6 +1509,7 @@ static void test_qemu_strtoi64_invalid(void) err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); g_assert_true(endptr == str); } @@ -1655,6 +1689,7 @@ static void test_qemu_strtoi64_full_null(void) err = qemu_strtoi64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 999); } static void test_qemu_strtoi64_full_empty(void) @@ -1666,6 +1701,7 @@ static void test_qemu_strtoi64_full_empty(void) err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi64_full_negative(void) @@ -1689,13 +1725,14 @@ static void test_qemu_strtoi64_full_trailing(void) err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 123); } static void test_qemu_strtoi64_full_max(void) { char *str = g_strdup_printf("%lld", LLONG_MAX); - int64_t res; + int64_t res = 999; int err; err = qemu_strtoi64(str, NULL, 0, &res); @@ -1730,6 +1767,7 @@ static void test_qemu_strtou64_null(void) err = qemu_strtou64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); g_assert_null(endptr); } @@ -1744,6 +1782,7 @@ static void test_qemu_strtou64_empty(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1758,6 +1797,7 @@ static void test_qemu_strtou64_whitespace(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1772,6 +1812,7 @@ static void test_qemu_strtou64_invalid(void) err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); g_assert_true(endptr == str); } @@ -1951,6 +1992,7 @@ static void test_qemu_strtou64_full_null(void) err = qemu_strtou64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 999); } static void test_qemu_strtou64_full_empty(void) @@ -1962,6 +2004,7 @@ static void test_qemu_strtou64_full_empty(void) err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtou64_full_negative(void) @@ -1985,6 +2028,7 @@ static void test_qemu_strtou64_full_trailing(void) err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, 18446744073709551614ULL); } static void test_qemu_strtou64_full_max(void) From 3069522bb9db4586262cbbbf447c5c5211d5921d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:26 -0500 Subject: [PATCH 06/21] test-cutils: Test more integer corner cases We have quite a few undertested and underdocumented integer parsing corner cases. To ensure that any changes we make in the code are intentional rather than accidental semantic changes, it is time to add more unit tests of existing behavior. In particular, this demonstrates that parse_uint() and qemu_strtou64() behave differently. For "-0", it's hard to argue why parse_uint needs to reject it (it's not a negative integer), but the documentation sort of mentions it; but it is intentional that all other negative values are treated as ERANGE with value 0 (compared to qemu_strtou64() treating "-2" as success and UINT64_MAX-1, for example). Also, when mixing overflow/underflow with a check for no trailing junk, parse_uint_full favors ERANGE over EINVAL, while qemu_strto[iu]* favor EINVAL. This behavior is outside the C standard, so we can pick whatever we want, but it would be nice to be consistent. Note that C requires that "9223372036854775808" fail strtoll() with ERANGE/INT64_MAX, but "-9223372036854775808" pass with INT64_MIN; we weren't testing this. For strtol(), the behavior depends on whether long is 32- or 64-bits (the cutoff point either being the same as strtoll() or at "-2147483648"). Meanwhile, C is clear that "-18446744073709551615" pass stroull() (but not strtoll) with value 1, even though we want it to fail parse_uint(). And although qemu_strtoui() has no C counterpart, it makes more sense if we design it like 32-bit strtoul() (that is, where "-4294967296" be an alternate acceptable spelling for "1", but "-0xffffffff00000001" should be treated as overflow and return 0xffffffff rather than 1). We aren't there yet, so some of the tests added in this patch have FIXME comments. However, note that C2x will (likely) be adding a SILENT semantic change, where C17 strtol("0b1", &ep, 2) returns 0 with ep="b1", but C2x will have it return 1 with ep="". I did not feel like adding testing for those corner cases, in part because the next version of C is not standard and libc support for binary parsing is not yet wide-spread (as of this patch, glibc.git still misparses bare "0b": https://sourceware.org/bugzilla/show_bug.cgi?id=30371). Message-Id: <20230522190441.64278-5-eblake@redhat.com> [eblake: fix a few typos spotted by Hanna] Reviewed-by: Hanna Czenczek [eblake: fix typo on platforms with 32-bit long] Signed-off-by: Eric Blake --- tests/unit/test-cutils.c | 959 +++++++++++++++++++++++++++++++++++---- 1 file changed, 879 insertions(+), 80 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 1eeaf21ae2..30cb5f122c 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -150,7 +150,6 @@ static void test_parse_uint_decimal(void) g_assert_true(endptr == str + strlen(str)); } - static void test_parse_uint_llong_max(void) { unsigned long long i = 999; @@ -168,27 +167,87 @@ static void test_parse_uint_llong_max(void) g_free(str); } -static void test_parse_uint_overflow(void) +static void test_parse_uint_max(void) { unsigned long long i = 999; char f = 'X'; char *endptr = &f; - const char *str = "99999999999999999999999999999999999999"; + char *str = g_strdup_printf("%llu", ULLONG_MAX); int r; r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, 0); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + g_free(str); +} + +static void test_parse_uint_overflow(void) +{ + unsigned long long i; + char f = 'X'; + char *endptr; + const char *str; + int r; + + i = 999; + endptr = &f; + str = "99999999999999999999999999999999999999"; + r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = &f; + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, ULLONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = &f; + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + r = parse_uint(str, &i, &endptr, 0); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); } static void test_parse_uint_negative(void) +{ + unsigned long long i; + char f = 'X'; + char *endptr; + const char *str; + int r; + + i = 999; + endptr = &f; + str = " \t -321"; + r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str + strlen(str)); + + i = 999; + endptr = &f; + str = "-0xffffffff00000001"; + r = parse_uint(str, &i, &endptr, 0); + g_assert_cmpint(r, ==, -ERANGE); + g_assert_cmpuint(i, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_parse_uint_negzero(void) { unsigned long long i = 999; char f = 'X'; char *endptr = &f; - const char *str = " \t -321"; + const char *str = " -0"; int r; r = parse_uint(str, &i, &endptr, 0); @@ -198,7 +257,6 @@ static void test_parse_uint_negative(void) g_assert_true(endptr == str + strlen(str)); } - static void test_parse_uint_full_trailing(void) { unsigned long long i = 999; @@ -223,6 +281,19 @@ static void test_parse_uint_full_correct(void) g_assert_cmpuint(i, ==, 123); } +static void test_parse_uint_full_erange_junk(void) +{ + /* FIXME - inconsistent with qemu_strto* which favors EINVAL */ + unsigned long long i = 999; + const char *str = "-2junk"; + int r; + + r = parse_uint_full(str, &i, 0); + + g_assert_cmpint(r, ==, -ERANGE /* FIXME -EINVAL */); + g_assert_cmpuint(i, ==, 0); +} + static void test_qemu_strtoi_correct(void) { const char *str = "12345 foo"; @@ -410,39 +481,55 @@ static void test_qemu_strtoi_max(void) static void test_qemu_strtoi_overflow(void) { - char *str = g_strdup_printf("%lld", (long long)INT_MAX + 1ll); - char f = 'X'; - const char *endptr = &f; - int res = 999; + const char *str; + const char *endptr; + int res; int err; + str = "2147483648"; /* INT_MAX + 1ll */ + endptr = "somewhere"; + res = 999; err = qemu_strtoi(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, INT_MAX); g_assert_true(endptr == str + strlen(str)); - g_free(str); -} - -static void test_qemu_strtoi_underflow(void) -{ - char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); - char f = 'X'; - const char *endptr = &f; - int res = 999; - int err; + str = "0x7fffffffffffffff"; /* LLONG_MAX */ + endptr = "somewhere"; + res = 999; err = qemu_strtoi(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, INT_MIN); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MAX); g_assert_true(endptr == str + strlen(str)); - g_free(str); } -static void test_qemu_strtoi_negative(void) +static void test_qemu_strtoi_min(void) { - const char *str = " \t -321"; + char *str = g_strdup_printf("%d", INT_MIN); char f = 'X'; const char *endptr = &f; int res = 999; @@ -450,9 +537,105 @@ static void test_qemu_strtoi_negative(void) err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtoi_underflow(void) +{ + const char *str; + const char *endptr; + int res; + int err; + + str = "-2147483649"; /* INT_MIN - 1ll */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x7fffffffffffffff"; /* -LLONG_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-18446744073709551615"; /* -UINT64_MAX (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_negative(void) +{ + const char *str; + const char *endptr; + int res; + int err; + + str = " \t -321"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); g_assert_true(endptr == str + strlen(str)); + + str = "-2147483648"; /* INT_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, INT_MIN); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + int res = 999; + int err; + + err = qemu_strtoi(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoi_full_correct(void) @@ -505,6 +688,18 @@ static void test_qemu_strtoi_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtoi_full_negzero(void) +{ + const char *str = " -0"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtoi_full_trailing(void) { const char *str = "123xxx"; @@ -530,6 +725,19 @@ static void test_qemu_strtoi_full_max(void) g_free(str); } +static void test_qemu_strtoi_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-9999999999junk"; + int res = 999; + int err; + + err = qemu_strtoi(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, INT_MIN); +} + static void test_qemu_strtoui_correct(void) { const char *str = "12345 foo"; @@ -699,6 +907,22 @@ static void test_qemu_strtoui_hex(void) g_assert_true(endptr == str + 1); } +static void test_qemu_strtoui_wrap(void) +{ + /* FIXME - wraparound should be consistent with 32-bit strtoul */ + const char *str = "-4294967295"; /* 1 mod 2^32 */ + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, -ERANGE /* FIXME 0 */); + g_assert_cmphex(res, ==, UINT_MAX /* FIXME 1 */); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoui_max(void) { char *str = g_strdup_printf("%u", UINT_MAX); @@ -717,34 +941,116 @@ static void test_qemu_strtoui_max(void) static void test_qemu_strtoui_overflow(void) { - char *str = g_strdup_printf("%lld", (long long)UINT_MAX + 1ll); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; + const char *str; + const char *endptr; + unsigned int res; int err; + str = "4294967296"; /* UINT_MAX + 1ll */ + endptr = "somewhere"; + res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, UINT_MAX); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x7fffffffffffffff"; /* LLONG_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x8000000000000000"; /* (uint64_t)LLONG_MIN */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0xffffffff00000001"; /* ULLONG_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + /* FIXME - overflow should be consistent with 32-bit strtoul */ + str = "0xfffffffffffffffe"; /* ULLONG_MAX - 1 (not UINT_MAX - 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); + g_assert_cmpuint(res, ==, UINT_MAX - 1 /* FIXME UINT_MAX */); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); - g_free(str); } static void test_qemu_strtoui_underflow(void) { - char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll); - char f = 'X'; - const char *endptr = &f; - unsigned int res = 999; + const char *str; + const char *endptr; + unsigned int res; int err; + str = "-4294967296"; /* -(long long)UINT_MAX - 1ll */ + endptr = "somewhere"; + res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpuint(res, ==, (unsigned int)-1); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + /* FIXME - overflow should be consistent with 32-bit strtoul */ + str = "-18446744073709551615"; /* -UINT64_MAX (not -(-1)) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); + g_assert_cmpuint(res, ==, 1 /* FIXME UINT_MAX */); + g_assert_true(endptr == str + strlen(str)); + + str = "-0xffffffff00000002"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); + g_assert_cmpuint(res, ==, UINT_MAX - 1 /* FIXME UINT_MAX */); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 32-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoui(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); - g_free(str); } static void test_qemu_strtoui_negative(void) @@ -762,6 +1068,21 @@ static void test_qemu_strtoui_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtoui_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoui_full_correct(void) { const char *str = "123"; @@ -808,6 +1129,17 @@ static void test_qemu_strtoui_full_negative(void) g_assert_cmpuint(res, ==, (unsigned int)-321); } +static void test_qemu_strtoui_full_negzero(void) +{ + const char *str = " -0"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtoui_full_trailing(void) { const char *str = "123xxx"; @@ -833,6 +1165,19 @@ static void test_qemu_strtoui_full_max(void) g_free(str); } +static void test_qemu_strtoui_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-9999999999junk"; + unsigned int res = 999; + int err; + + err = qemu_strtoui(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, UINT_MAX); +} + static void test_qemu_strtol_correct(void) { const char *str = "12345 foo"; @@ -1020,22 +1365,50 @@ static void test_qemu_strtol_max(void) static void test_qemu_strtol_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - long res = 999; + const char *str; + const char *endptr; + long res; int err; + /* 1 more than LONG_MAX */ + str = LONG_MAX == INT_MAX ? "2147483648" : "9223372036854775808"; + endptr = "somewhere"; + res = 999; err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + if (LONG_MAX == INT_MAX) { + str = "0xffffffff00000001"; /* ULLONG_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MAX); g_assert_true(endptr == str + strlen(str)); } -static void test_qemu_strtol_underflow(void) +static void test_qemu_strtol_min(void) { - const char *str = "-99999999999999999999999999999999999999999999"; + char *str = g_strdup_printf("%ld", LONG_MIN); char f = 'X'; const char *endptr = &f; long res = 999; @@ -1043,6 +1416,50 @@ static void test_qemu_strtol_underflow(void) err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + g_free(str); +} + +static void test_qemu_strtol_underflow(void) +{ + const char *str; + const char *endptr; + long res; + int err; + + /* 1 less than LONG_MIN */ + str = LONG_MIN == INT_MIN ? "-2147483649" : "-9223372036854775809"; + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "-18446744073709551615"; /* -UINT64_MAX (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + } + + str = "-0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, LONG_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LONG_MIN); g_assert_true(endptr == str + strlen(str)); @@ -1063,6 +1480,21 @@ static void test_qemu_strtol_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtol_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + long res = 999; + int err; + + err = qemu_strtol(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtol_full_correct(void) { const char *str = "123"; @@ -1113,6 +1545,18 @@ static void test_qemu_strtol_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtol_full_negzero(void) +{ + const char *str = " -0"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtol_full_trailing(void) { const char *str = "123xxx"; @@ -1138,6 +1582,19 @@ static void test_qemu_strtol_full_max(void) g_free(str); } +static void test_qemu_strtol_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + long res = 999; + int err; + + err = qemu_strtol(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, LONG_MIN); +} + static void test_qemu_strtoul_correct(void) { const char *str = "12345 foo"; @@ -1307,6 +1764,23 @@ static void test_qemu_strtoul_hex(void) g_assert_true(endptr == str + 1); } +static void test_qemu_strtoul_wrap(void) +{ + const char *str; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + /* 1 mod 2^(sizeof(long)*8) */ + str = LONG_MAX == INT_MAX ? "-4294967295" : "-18446744073709551615"; + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 1); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoul_max(void) { char *str = g_strdup_printf("%lu", ULONG_MAX); @@ -1325,31 +1799,87 @@ static void test_qemu_strtoul_max(void) static void test_qemu_strtoul_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; + const char *str; + const char *endptr; + unsigned long res; int err; + /* 1 more than ULONG_MAX */ + str = ULONG_MAX == UINT_MAX ? "4294967296" : "18446744073709551616"; + endptr = "somewhere"; + res = 999; err = qemu_strtoul(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, ULONG_MAX); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "0xffffffff00000001"; /* UINT64_MAX - UINT_MAX + 1 (not 1) */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtoul_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - unsigned long res = 999; + const char *str; + const char *endptr; + unsigned long res; int err; + /* 1 less than -ULONG_MAX */ + str = ULONG_MAX == UINT_MAX ? "-4294967296" : "-18446744073709551616"; + endptr = "somewhere"; + res = 999; err = qemu_strtoul(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpuint(res, ==, -1ul); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + if (LONG_MAX == INT_MAX) { + str = "-0xffffffff00000002"; + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + } + + str = "-0x10000000000000000"; /* 65 bits, either sign bit position clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, either sign bit position set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoul(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, ULONG_MAX); g_assert_true(endptr == str + strlen(str)); } @@ -1368,6 +1898,21 @@ static void test_qemu_strtoul_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtoul_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoul_full_correct(void) { const char *str = "123"; @@ -1414,6 +1959,17 @@ static void test_qemu_strtoul_full_negative(void) g_assert_cmpuint(res, ==, -321ul); } +static void test_qemu_strtoul_full_negzero(void) +{ + const char *str = " -0"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtoul_full_trailing(void) { const char *str = "123xxx"; @@ -1439,6 +1995,19 @@ static void test_qemu_strtoul_full_max(void) g_free(str); } +static void test_qemu_strtoul_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + unsigned long res = 999; + int err; + + err = qemu_strtoul(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, ULONG_MAX); +} + static void test_qemu_strtoi64_correct(void) { const char *str = "12345 foo"; @@ -1626,7 +2195,39 @@ static void test_qemu_strtoi64_max(void) static void test_qemu_strtoi64_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; + const char *str; + const char *endptr; + int64_t res; + int err; + + str = "9223372036854775808"; /* 1 more than INT64_MAX */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MAX); + g_assert_true(endptr == str + strlen(str)); +} + +static void test_qemu_strtoi64_min(void) +{ + char *str = g_strdup_printf("%lld", LLONG_MIN); char f = 'X'; const char *endptr = &f; int64_t res = 999; @@ -1634,23 +2235,41 @@ static void test_qemu_strtoi64_overflow(void) err = qemu_strtoi64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LLONG_MAX); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, LLONG_MIN); g_assert_true(endptr == str + strlen(str)); + g_free(str); } static void test_qemu_strtoi64_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - int64_t res = 999; + const char *str; + const char *endptr; + int64_t res; int err; + str = "-9223372036854775809"; /* 1 less than INT64_MIN */ + endptr = "somewhere"; + res = 999; err = qemu_strtoi64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, LLONG_MIN); + g_assert_cmpint(res, ==, INT64_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MIN); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtoi64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpint(res, ==, INT64_MIN); g_assert_true(endptr == str + strlen(str)); } @@ -1669,6 +2288,21 @@ static void test_qemu_strtoi64_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtoi64_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtoi64_full_correct(void) { const char *str = "123"; @@ -1716,6 +2350,18 @@ static void test_qemu_strtoi64_full_negative(void) g_assert_cmpint(res, ==, -321); } +static void test_qemu_strtoi64_full_negzero(void) +{ + const char *str = " -0"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); +} + static void test_qemu_strtoi64_full_trailing(void) { const char *str = "123xxx"; @@ -1742,6 +2388,19 @@ static void test_qemu_strtoi64_full_max(void) g_free(str); } +static void test_qemu_strtoi64_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + int64_t res = 999; + int err; + + err = qemu_strtoi64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, INT64_MIN); +} + static void test_qemu_strtou64_correct(void) { const char *str = "12345 foo"; @@ -1911,6 +2570,21 @@ static void test_qemu_strtou64_hex(void) g_assert_true(endptr == str + 1); } +static void test_qemu_strtou64_wrap(void) +{ + const char *str = "-18446744073709551615"; /* 1 mod 2^64 */ + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 1); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtou64_max(void) { char *str = g_strdup_printf("%llu", ULLONG_MAX); @@ -1929,31 +2603,65 @@ static void test_qemu_strtou64_max(void) static void test_qemu_strtou64_overflow(void) { - const char *str = "99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; + const char *str; + const char *endptr; + uint64_t res; int err; + str = "18446744073709551616"; /* 1 more than UINT64_MAX */ + endptr = "somewhere"; + res = 999; err = qemu_strtou64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, ULLONG_MAX); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); g_assert_true(endptr == str + strlen(str)); } static void test_qemu_strtou64_underflow(void) { - const char *str = "-99999999999999999999999999999999999999999999"; - char f = 'X'; - const char *endptr = &f; - uint64_t res = 999; + const char *str; + const char *endptr; + uint64_t res; int err; + str = "-99999999999999999999999999999999999999999999"; + endptr = "somewhere"; + res = 999; err = qemu_strtou64(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, -1ull); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); + g_assert_true(endptr == str + strlen(str)); + + str = "-0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ + endptr = "somewhere"; + res = 999; + err = qemu_strtou64(str, &endptr, 0, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT64_MAX); g_assert_true(endptr == str + strlen(str)); } @@ -1972,6 +2680,21 @@ static void test_qemu_strtou64_negative(void) g_assert_true(endptr == str + strlen(str)); } +static void test_qemu_strtou64_negzero(void) +{ + const char *str = " -0"; + char f = 'X'; + const char *endptr = &f; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, &endptr, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); + g_assert_true(endptr == str + strlen(str)); +} + static void test_qemu_strtou64_full_correct(void) { const char *str = "18446744073709551614"; @@ -2019,6 +2742,18 @@ static void test_qemu_strtou64_full_negative(void) g_assert_cmpuint(res, ==, -321ull); } +static void test_qemu_strtou64_full_negzero(void) +{ + const char *str = " -0"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, 0); + g_assert_cmpuint(res, ==, 0); +} + static void test_qemu_strtou64_full_trailing(void) { const char *str = "18446744073709551614xxxxxx"; @@ -2044,6 +2779,19 @@ static void test_qemu_strtou64_full_max(void) g_free(str); } +static void test_qemu_strtou64_full_erange_junk(void) +{ + /* EINVAL has priority over ERANGE */ + const char *str = "-99999999999999999999junk"; + uint64_t res = 999; + int err; + + err = qemu_strtou64(str, NULL, 0, &res); + + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpuint(res, ==, UINT64_MAX); +} + static void test_qemu_strtosz_simple(void) { const char *str; @@ -2585,12 +3333,16 @@ int main(int argc, char **argv) g_test_add_func("/cutils/parse_uint/octal", test_parse_uint_octal); g_test_add_func("/cutils/parse_uint/decimal", test_parse_uint_decimal); g_test_add_func("/cutils/parse_uint/llong_max", test_parse_uint_llong_max); + g_test_add_func("/cutils/parse_uint/max", test_parse_uint_max); g_test_add_func("/cutils/parse_uint/overflow", test_parse_uint_overflow); g_test_add_func("/cutils/parse_uint/negative", test_parse_uint_negative); + g_test_add_func("/cutils/parse_uint/negzero", test_parse_uint_negzero); g_test_add_func("/cutils/parse_uint_full/trailing", test_parse_uint_full_trailing); g_test_add_func("/cutils/parse_uint_full/correct", test_parse_uint_full_correct); + g_test_add_func("/cutils/parse_uint_full/erange_junk", + test_parse_uint_full_erange_junk); /* qemu_strtoi() tests */ g_test_add_func("/cutils/qemu_strtoi/correct", @@ -2615,10 +3367,14 @@ int main(int argc, char **argv) test_qemu_strtoi_max); g_test_add_func("/cutils/qemu_strtoi/overflow", test_qemu_strtoi_overflow); + g_test_add_func("/cutils/qemu_strtoi/min", + test_qemu_strtoi_min); g_test_add_func("/cutils/qemu_strtoi/underflow", test_qemu_strtoi_underflow); g_test_add_func("/cutils/qemu_strtoi/negative", test_qemu_strtoi_negative); + g_test_add_func("/cutils/qemu_strtoi/negzero", + test_qemu_strtoi_negzero); g_test_add_func("/cutils/qemu_strtoi_full/correct", test_qemu_strtoi_full_correct); g_test_add_func("/cutils/qemu_strtoi_full/null", @@ -2627,10 +3383,14 @@ int main(int argc, char **argv) test_qemu_strtoi_full_empty); g_test_add_func("/cutils/qemu_strtoi_full/negative", test_qemu_strtoi_full_negative); + g_test_add_func("/cutils/qemu_strtoi_full/negzero", + test_qemu_strtoi_full_negzero); g_test_add_func("/cutils/qemu_strtoi_full/trailing", test_qemu_strtoi_full_trailing); g_test_add_func("/cutils/qemu_strtoi_full/max", test_qemu_strtoi_full_max); + g_test_add_func("/cutils/qemu_strtoi_full/erange_junk", + test_qemu_strtoi_full_erange_junk); /* qemu_strtoui() tests */ g_test_add_func("/cutils/qemu_strtoui/correct", @@ -2651,6 +3411,8 @@ int main(int argc, char **argv) test_qemu_strtoui_decimal); g_test_add_func("/cutils/qemu_strtoui/hex", test_qemu_strtoui_hex); + g_test_add_func("/cutils/qemu_strtoui/wrap", + test_qemu_strtoui_wrap); g_test_add_func("/cutils/qemu_strtoui/max", test_qemu_strtoui_max); g_test_add_func("/cutils/qemu_strtoui/overflow", @@ -2659,6 +3421,8 @@ int main(int argc, char **argv) test_qemu_strtoui_underflow); g_test_add_func("/cutils/qemu_strtoui/negative", test_qemu_strtoui_negative); + g_test_add_func("/cutils/qemu_strtoui/negzero", + test_qemu_strtoui_negzero); g_test_add_func("/cutils/qemu_strtoui_full/correct", test_qemu_strtoui_full_correct); g_test_add_func("/cutils/qemu_strtoui_full/null", @@ -2667,10 +3431,14 @@ int main(int argc, char **argv) test_qemu_strtoui_full_empty); g_test_add_func("/cutils/qemu_strtoui_full/negative", test_qemu_strtoui_full_negative); + g_test_add_func("/cutils/qemu_strtoui_full/negzero", + test_qemu_strtoui_full_negzero); g_test_add_func("/cutils/qemu_strtoui_full/trailing", test_qemu_strtoui_full_trailing); g_test_add_func("/cutils/qemu_strtoui_full/max", test_qemu_strtoui_full_max); + g_test_add_func("/cutils/qemu_strtoui_full/erange_junk", + test_qemu_strtoui_full_erange_junk); /* qemu_strtol() tests */ g_test_add_func("/cutils/qemu_strtol/correct", @@ -2695,10 +3463,14 @@ int main(int argc, char **argv) test_qemu_strtol_max); g_test_add_func("/cutils/qemu_strtol/overflow", test_qemu_strtol_overflow); + g_test_add_func("/cutils/qemu_strtol/min", + test_qemu_strtol_min); g_test_add_func("/cutils/qemu_strtol/underflow", test_qemu_strtol_underflow); g_test_add_func("/cutils/qemu_strtol/negative", test_qemu_strtol_negative); + g_test_add_func("/cutils/qemu_strtol/negzero", + test_qemu_strtol_negzero); g_test_add_func("/cutils/qemu_strtol_full/correct", test_qemu_strtol_full_correct); g_test_add_func("/cutils/qemu_strtol_full/null", @@ -2707,10 +3479,14 @@ int main(int argc, char **argv) test_qemu_strtol_full_empty); g_test_add_func("/cutils/qemu_strtol_full/negative", test_qemu_strtol_full_negative); + g_test_add_func("/cutils/qemu_strtol_full/negzero", + test_qemu_strtol_full_negzero); g_test_add_func("/cutils/qemu_strtol_full/trailing", test_qemu_strtol_full_trailing); g_test_add_func("/cutils/qemu_strtol_full/max", test_qemu_strtol_full_max); + g_test_add_func("/cutils/qemu_strtol_full/erange_junk", + test_qemu_strtol_full_erange_junk); /* qemu_strtoul() tests */ g_test_add_func("/cutils/qemu_strtoul/correct", @@ -2731,6 +3507,8 @@ int main(int argc, char **argv) test_qemu_strtoul_decimal); g_test_add_func("/cutils/qemu_strtoul/hex", test_qemu_strtoul_hex); + g_test_add_func("/cutils/qemu_strtoul/wrap", + test_qemu_strtoul_wrap); g_test_add_func("/cutils/qemu_strtoul/max", test_qemu_strtoul_max); g_test_add_func("/cutils/qemu_strtoul/overflow", @@ -2739,6 +3517,8 @@ int main(int argc, char **argv) test_qemu_strtoul_underflow); g_test_add_func("/cutils/qemu_strtoul/negative", test_qemu_strtoul_negative); + g_test_add_func("/cutils/qemu_strtoul/negzero", + test_qemu_strtoul_negzero); g_test_add_func("/cutils/qemu_strtoul_full/correct", test_qemu_strtoul_full_correct); g_test_add_func("/cutils/qemu_strtoul_full/null", @@ -2747,10 +3527,14 @@ int main(int argc, char **argv) test_qemu_strtoul_full_empty); g_test_add_func("/cutils/qemu_strtoul_full/negative", test_qemu_strtoul_full_negative); + g_test_add_func("/cutils/qemu_strtoul_full/negzero", + test_qemu_strtoul_full_negzero); g_test_add_func("/cutils/qemu_strtoul_full/trailing", test_qemu_strtoul_full_trailing); g_test_add_func("/cutils/qemu_strtoul_full/max", test_qemu_strtoul_full_max); + g_test_add_func("/cutils/qemu_strtoul_full/erange_junk", + test_qemu_strtoul_full_erange_junk); /* qemu_strtoi64() tests */ g_test_add_func("/cutils/qemu_strtoi64/correct", @@ -2761,8 +3545,7 @@ int main(int argc, char **argv) test_qemu_strtoi64_empty); g_test_add_func("/cutils/qemu_strtoi64/whitespace", test_qemu_strtoi64_whitespace); - g_test_add_func("/cutils/qemu_strtoi64/invalid" - , + g_test_add_func("/cutils/qemu_strtoi64/invalid", test_qemu_strtoi64_invalid); g_test_add_func("/cutils/qemu_strtoi64/trailing", test_qemu_strtoi64_trailing); @@ -2776,10 +3559,14 @@ int main(int argc, char **argv) test_qemu_strtoi64_max); g_test_add_func("/cutils/qemu_strtoi64/overflow", test_qemu_strtoi64_overflow); + g_test_add_func("/cutils/qemu_strtoi64/min", + test_qemu_strtoi64_min); g_test_add_func("/cutils/qemu_strtoi64/underflow", test_qemu_strtoi64_underflow); g_test_add_func("/cutils/qemu_strtoi64/negative", test_qemu_strtoi64_negative); + g_test_add_func("/cutils/qemu_strtoi64/negzero", + test_qemu_strtoi64_negzero); g_test_add_func("/cutils/qemu_strtoi64_full/correct", test_qemu_strtoi64_full_correct); g_test_add_func("/cutils/qemu_strtoi64_full/null", @@ -2788,10 +3575,14 @@ int main(int argc, char **argv) test_qemu_strtoi64_full_empty); g_test_add_func("/cutils/qemu_strtoi64_full/negative", test_qemu_strtoi64_full_negative); + g_test_add_func("/cutils/qemu_strtoi64_full/negzero", + test_qemu_strtoi64_full_negzero); g_test_add_func("/cutils/qemu_strtoi64_full/trailing", test_qemu_strtoi64_full_trailing); g_test_add_func("/cutils/qemu_strtoi64_full/max", test_qemu_strtoi64_full_max); + g_test_add_func("/cutils/qemu_strtoi64_full/erange_junk", + test_qemu_strtoi64_full_erange_junk); /* qemu_strtou64() tests */ g_test_add_func("/cutils/qemu_strtou64/correct", @@ -2812,6 +3603,8 @@ int main(int argc, char **argv) test_qemu_strtou64_decimal); g_test_add_func("/cutils/qemu_strtou64/hex", test_qemu_strtou64_hex); + g_test_add_func("/cutils/qemu_strtou64/wrap", + test_qemu_strtou64_wrap); g_test_add_func("/cutils/qemu_strtou64/max", test_qemu_strtou64_max); g_test_add_func("/cutils/qemu_strtou64/overflow", @@ -2820,6 +3613,8 @@ int main(int argc, char **argv) test_qemu_strtou64_underflow); g_test_add_func("/cutils/qemu_strtou64/negative", test_qemu_strtou64_negative); + g_test_add_func("/cutils/qemu_strtou64/negzero", + test_qemu_strtou64_negzero); g_test_add_func("/cutils/qemu_strtou64_full/correct", test_qemu_strtou64_full_correct); g_test_add_func("/cutils/qemu_strtou64_full/null", @@ -2828,10 +3623,14 @@ int main(int argc, char **argv) test_qemu_strtou64_full_empty); g_test_add_func("/cutils/qemu_strtou64_full/negative", test_qemu_strtou64_full_negative); + g_test_add_func("/cutils/qemu_strtou64_full/negzero", + test_qemu_strtou64_full_negzero); g_test_add_func("/cutils/qemu_strtou64_full/trailing", test_qemu_strtou64_full_trailing); g_test_add_func("/cutils/qemu_strtou64_full/max", test_qemu_strtou64_full_max); + g_test_add_func("/cutils/qemu_strtou64_full/erange_junk", + test_qemu_strtou64_full_erange_junk); g_test_add_func("/cutils/strtosz/simple", test_qemu_strtosz_simple); From 56ddafde3f68b27123281dde3f48b5e945c86b48 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:27 -0500 Subject: [PATCH 07/21] cutils: Fix wraparound parsing in qemu_strtoui While we were matching 32-bit strtol in qemu_strtoi, our use of a 64-bit parse was leaking through for some inaccurate answers in qemu_strtoui in comparison to a 32-bit strtoul (see the unit test for examples). The comment for that function even described what we have to do for a correct parse, but didn't implement it correctly: since strtoull checks for overflow against the wrong values and then negates, we have to temporarily undo negation before checking for overflow against our desired value. Our int wrappers would be a lot easier to write if libc had a guaranteed 32-bit parser even on platforms with 64-bit long. Whether we parse C2x binary strings like "0b1000" is currently up to what libc does; our unit tests intentionally don't cover that at the moment, though. Fixes: 473a2a331e ("cutils: add qemu_strtoi & qemu_strtoui parsers for int/unsigned int types", v2.12.0) Signed-off-by: Eric Blake CC: qemu-stable@nongnu.org Message-Id: <20230522190441.64278-6-eblake@redhat.com> Reviewed-by: Hanna Czenczek --- tests/unit/test-cutils.c | 20 +++++++++----------- util/cutils.c | 25 +++++++++++++++++++------ 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 30cb5f122c..8d2e057bda 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -909,7 +909,7 @@ static void test_qemu_strtoui_hex(void) static void test_qemu_strtoui_wrap(void) { - /* FIXME - wraparound should be consistent with 32-bit strtoul */ + /* wraparound is consistent with 32-bit strtoul */ const char *str = "-4294967295"; /* 1 mod 2^32 */ char f = 'X'; const char *endptr = &f; @@ -918,8 +918,8 @@ static void test_qemu_strtoui_wrap(void) err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, -ERANGE /* FIXME 0 */); - g_assert_cmphex(res, ==, UINT_MAX /* FIXME 1 */); + g_assert_cmpint(err, ==, 0); + g_assert_cmphex(res, ==, 1); g_assert_true(endptr == str + strlen(str)); } @@ -978,13 +978,12 @@ static void test_qemu_strtoui_overflow(void) g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); - /* FIXME - overflow should be consistent with 32-bit strtoul */ str = "0xfffffffffffffffe"; /* ULLONG_MAX - 1 (not UINT_MAX - 1) */ endptr = "somewhere"; res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); - g_assert_cmpuint(res, ==, UINT_MAX - 1 /* FIXME UINT_MAX */); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); str = "0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ @@ -1019,21 +1018,20 @@ static void test_qemu_strtoui_underflow(void) g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); - /* FIXME - overflow should be consistent with 32-bit strtoul */ str = "-18446744073709551615"; /* -UINT64_MAX (not -(-1)) */ endptr = "somewhere"; res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); - g_assert_cmpuint(res, ==, 1 /* FIXME UINT_MAX */); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); str = "-0xffffffff00000002"; endptr = "somewhere"; res = 999; err = qemu_strtoui(str, &endptr, 0, &res); - g_assert_cmpint(err, ==, 0 /* FIXME -ERANGE */); - g_assert_cmpuint(res, ==, UINT_MAX - 1 /* FIXME UINT_MAX */); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpuint(res, ==, UINT_MAX); g_assert_true(endptr == str + strlen(str)); str = "-0x10000000000000000"; /* 65 bits, 32-bit sign bit clear */ diff --git a/util/cutils.c b/util/cutils.c index 5887e74414..9b6ce9179c 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -391,6 +391,9 @@ static int check_strtox_error(const char *nptr, char *ep, * and return -ERANGE. * * Else store the converted value in @result, and return zero. + * + * This matches the behavior of strtol() on 32-bit platforms, even on + * platforms where long is 64-bits. */ int qemu_strtoi(const char *nptr, const char **endptr, int base, int *result) @@ -443,13 +446,15 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, * * Note that a number with a leading minus sign gets converted without * the minus sign, checked for overflow (see above), then negated (in - * @result's type). This is exactly how strtoul() works. + * @result's type). This matches the behavior of strtoul() on 32-bit + * platforms, even on platforms where long is 64-bits. */ int qemu_strtoui(const char *nptr, const char **endptr, int base, unsigned int *result) { char *ep; - long long lresult; + unsigned long long lresult; + bool neg; assert((unsigned) base <= 36 && base != 1); if (!nptr) { @@ -466,14 +471,22 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, if (errno == ERANGE) { *result = -1; } else { + /* + * Note that platforms with 32-bit strtoul only accept input + * in the range [-4294967295, 4294967295]; but we used 64-bit + * strtoull which wraps -18446744073709551615 to 1 instead of + * declaring overflow. So we must check if '-' was parsed, + * and if so, undo the negation before doing our bounds check. + */ + neg = memchr(nptr, '-', ep - nptr) != NULL; + if (neg) { + lresult = -lresult; + } if (lresult > UINT_MAX) { *result = UINT_MAX; errno = ERANGE; - } else if (lresult < INT_MIN) { - *result = UINT_MAX; - errno = ERANGE; } else { - *result = lresult; + *result = neg ? -lresult : lresult; } } return check_strtox_error(nptr, ep, endptr, lresult == 0, errno); From 84760bbca9a4ef1bfb38b9c101a2604e5d429605 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:28 -0500 Subject: [PATCH 08/21] cutils: Document differences between parse_uint and qemu_strtou64 These two functions are subtly different, and not just because of swapped parameter order. It took me adding better unit tests to figure out why. Document the differences to make it more obvious to developers trying to pick which one to use, as well as to aid in upcoming semantic changes. While touching the documentation, adjust a mis-statement: parse_uint does not return -EINVAL on invalid base, but assert()s, like all the other qemu_strto* functions that take a base argument. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-7-eblake@redhat.com> --- util/cutils.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/util/cutils.c b/util/cutils.c index 9b6ce9179c..36c14b769f 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -611,6 +611,8 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, * Convert string @nptr to an uint64_t. * * Works like qemu_strtoul(), except it stores UINT64_MAX on overflow. + * (If you want to prohibit negative numbers that wrap around to + * positive, use parse_uint()). */ int qemu_strtou64(const char *nptr, const char **endptr, int base, uint64_t *result) @@ -721,7 +723,8 @@ const char *qemu_strchrnul(const char *s, int c) * * @s: String to parse * @value: Destination for parsed integer value - * @endptr: Destination for pointer to first character not consumed + * @endptr: Destination for pointer to first character not consumed, must + * not be %NULL * @base: integer base, between 2 and 36 inclusive, or 0 * * Parse unsigned integer @@ -729,15 +732,16 @@ const char *qemu_strchrnul(const char *s, int c) * Parsed syntax is like strtoull()'s: arbitrary whitespace, a single optional * '+' or '-', an optional "0x" if @base is 0 or 16, one or more digits. * - * If @s is null, or @base is invalid, or @s doesn't start with an - * integer in the syntax above, set *@value to 0, *@endptr to @s, and - * return -EINVAL. + * If @s is null, or @s doesn't start with an integer in the syntax + * above, set *@value to 0, *@endptr to @s, and return -EINVAL. * * Set *@endptr to point right beyond the parsed integer (even if the integer * overflows or is negative, all digits will be parsed and *@endptr will * point right beyond them). * * If the integer is negative, set *@value to 0, and return -ERANGE. + * (If you want to allow negative numbers that wrap around within + * bounds, use qemu_strtou64()). * * If the integer overflows unsigned long long, set *@value to * ULLONG_MAX, and return -ERANGE. @@ -794,10 +798,10 @@ out: * * Parse unsigned integer from entire string * - * Have the same behavior of parse_uint(), but with an additional check - * for additional data after the parsed number. If extra characters are present - * after the parsed number, the function will return -EINVAL, and *@v will - * be set to 0. + * Have the same behavior of parse_uint(), but with an additional + * check for additional data after the parsed number. If extra + * characters are present after a non-overflowing parsed number, the + * function will return -EINVAL, and *@v will be set to 0. */ int parse_uint_full(const char *s, unsigned long long *value, int base) { From bd1386cce1b184e4260721858d3bb4b4c888b5f0 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:29 -0500 Subject: [PATCH 09/21] cutils: Adjust signature of parse_uint[_full] It's already confusing that we have two very similar functions for wrapping the parse of a 64-bit unsigned value, differing mainly on whether they permit leading '-'. Adjust the signature of parse_uint() and parse_uint_full() to be like all of qemu_strto*(): put the result parameter last, use the same types (uint64_t and unsigned long long have the same width, but are not always the same type), and mark endptr const (this latter change only affects the rare caller of parse_uint). Adjust all callers in the tree. While at it, note that since cutils.c already includes: QEMU_BUILD_BUG_ON(sizeof(int64_t) != sizeof(long long)); we are guaranteed that the result of parse_uint* cannot exceed UINT64_MAX (or the build would have failed), so we can drop pre-existing dead comparisons in opts-visitor.c that were never false. Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-8-eblake@redhat.com> [eblake: Drop dead code spotted by Markus] Signed-off-by: Eric Blake --- audio/audio_legacy.c | 4 +- block/gluster.c | 4 +- block/nfs.c | 4 +- blockdev.c | 4 +- contrib/ivshmem-server/main.c | 4 +- include/qemu/cutils.h | 5 +- qapi/opts-visitor.c | 12 ++-- tests/unit/test-cutils.c | 119 +++++++++++++++------------------- ui/vnc.c | 4 +- util/cutils.c | 13 ++-- util/guest-random.c | 4 +- util/qemu-sockets.c | 10 +-- 12 files changed, 86 insertions(+), 101 deletions(-) diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c index b848001ff7..dc72ba55e9 100644 --- a/audio/audio_legacy.c +++ b/audio/audio_legacy.c @@ -35,8 +35,8 @@ static uint32_t toui32(const char *str) { - unsigned long long ret; - if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) { + uint64_t ret; + if (parse_uint_full(str, 10, &ret) || ret > UINT32_MAX) { dolog("Invalid integer value `%s'\n", str); exit(1); } diff --git a/block/gluster.c b/block/gluster.c index 185a83e5e5..ad5fadbe79 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -424,7 +424,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, int ret; int old_errno; SocketAddressList *server; - unsigned long long port; + uint64_t port; glfs = glfs_find_preopened(gconf->volume); if (glfs) { @@ -445,7 +445,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, server->value->u.q_unix.path, 0); break; case SOCKET_ADDRESS_TYPE_INET: - if (parse_uint_full(server->value->u.inet.port, &port, 10) < 0 || + if (parse_uint_full(server->value->u.inet.port, 10, &port) < 0 || port > 65535) { error_setg(errp, "'%s' is not a valid port number", server->value->u.inet.port); diff --git a/block/nfs.c b/block/nfs.c index 8f89ece69f..c24df49747 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -114,13 +114,13 @@ static int nfs_parse_uri(const char *filename, QDict *options, Error **errp) qdict_put_str(options, "path", uri->path); for (i = 0; i < qp->n; i++) { - unsigned long long val; + uint64_t val; if (!qp->p[i].value) { error_setg(errp, "Value for NFS parameter expected: %s", qp->p[i].name); goto out; } - if (parse_uint_full(qp->p[i].value, &val, 0)) { + if (parse_uint_full(qp->p[i].value, 0, &val)) { error_setg(errp, "Illegal value for NFS parameter: %s", qp->p[i].name); goto out; diff --git a/blockdev.c b/blockdev.c index db2725fe74..e6eba61484 100644 --- a/blockdev.c +++ b/blockdev.c @@ -341,10 +341,10 @@ static bool parse_stats_intervals(BlockAcctStats *stats, QList *intervals, switch (qobject_type(entry->value)) { case QTYPE_QSTRING: { - unsigned long long length; + uint64_t length; const char *str = qstring_get_str(qobject_to(QString, entry->value)); - if (parse_uint_full(str, &length, 10) == 0 && + if (parse_uint_full(str, 10, &length) == 0 && length > 0 && length <= UINT_MAX) { block_acct_add_interval(stats, (unsigned) length); } else { diff --git a/contrib/ivshmem-server/main.c b/contrib/ivshmem-server/main.c index 224dbeb547..5901f17707 100644 --- a/contrib/ivshmem-server/main.c +++ b/contrib/ivshmem-server/main.c @@ -69,7 +69,7 @@ static void ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) { int c; - unsigned long long v; + uint64_t v; Error *err = NULL; while ((c = getopt(argc, argv, "hvFp:S:m:M:l:n:")) != -1) { @@ -112,7 +112,7 @@ ivshmem_server_parse_args(IvshmemServerArgs *args, int argc, char *argv[]) break; case 'n': /* number of vectors */ - if (parse_uint_full(optarg, &v, 0) < 0) { + if (parse_uint_full(optarg, 0, &v) < 0) { fprintf(stderr, "cannot parse n_vectors\n"); ivshmem_server_help(argv[0]); exit(1); diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 92c436d8c7..92c927a6a3 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -163,9 +163,8 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, int qemu_strtod(const char *nptr, const char **endptr, double *result); int qemu_strtod_finite(const char *nptr, const char **endptr, double *result); -int parse_uint(const char *s, unsigned long long *value, char **endptr, - int base); -int parse_uint_full(const char *s, unsigned long long *value, int base); +int parse_uint(const char *s, const char **endptr, int base, uint64_t *value); +int parse_uint_full(const char *s, int base, uint64_t *value); int qemu_strtosz(const char *nptr, const char **end, uint64_t *result); int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result); diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 587f31baf6..8f1efab8b9 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -454,8 +454,8 @@ opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) OptsVisitor *ov = to_ov(v); const QemuOpt *opt; const char *str; - unsigned long long val; - char *endptr; + uint64_t val; + const char *endptr; if (ov->list_mode == LM_UNSIGNED_INTERVAL) { *obj = ov->range_next.u; @@ -471,18 +471,18 @@ opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) /* we've gotten past lookup_scalar() */ assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); - if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { + if (parse_uint(str, &endptr, 0, &val) == 0) { if (*endptr == '\0') { *obj = val; processed(ov, name); return true; } if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { - unsigned long long val2; + uint64_t val2; str = endptr + 1; - if (parse_uint_full(str, &val2, 0) == 0 && - val2 <= UINT64_MAX && val <= val2 && + if (parse_uint_full(str, 0, &val2) == 0 && + val <= val2 && val2 - val < OPTS_VISITOR_RANGE_MAX) { ov->range_next.u = val; ov->range_limit.u = val2; diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 8d2e057bda..3664b4fffc 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -31,12 +31,11 @@ static void test_parse_uint_null(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; int r; - r = parse_uint(NULL, &i, &endptr, 0); + r = parse_uint(NULL, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -45,13 +44,12 @@ static void test_parse_uint_null(void) static void test_parse_uint_empty(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = ""; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -60,13 +58,12 @@ static void test_parse_uint_empty(void) static void test_parse_uint_whitespace(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = " \t "; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -76,13 +73,12 @@ static void test_parse_uint_whitespace(void) static void test_parse_uint_invalid(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = " \t xxx"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -92,13 +88,12 @@ static void test_parse_uint_invalid(void) static void test_parse_uint_trailing(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "123xxx"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 123); @@ -107,13 +102,12 @@ static void test_parse_uint_trailing(void) static void test_parse_uint_correct(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "123"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 123); @@ -122,13 +116,12 @@ static void test_parse_uint_correct(void) static void test_parse_uint_octal(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "0123"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 0123); @@ -137,13 +130,12 @@ static void test_parse_uint_octal(void) static void test_parse_uint_decimal(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = "0123"; int r; - r = parse_uint(str, &i, &endptr, 10); + r = parse_uint(str, &endptr, 10, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 123); @@ -152,13 +144,12 @@ static void test_parse_uint_decimal(void) static void test_parse_uint_llong_max(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; char *str = g_strdup_printf("%llu", (unsigned long long)LLONG_MAX + 1); int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, (unsigned long long)LLONG_MAX + 1); @@ -169,13 +160,12 @@ static void test_parse_uint_llong_max(void) static void test_parse_uint_max(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; char *str = g_strdup_printf("%llu", ULLONG_MAX); int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, ULLONG_MAX); @@ -186,32 +176,31 @@ static void test_parse_uint_max(void) static void test_parse_uint_overflow(void) { - unsigned long long i; - char f = 'X'; - char *endptr; + uint64_t i; + const char *endptr; const char *str; int r; i = 999; - endptr = &f; + endptr = "somewhere"; str = "99999999999999999999999999999999999999"; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); i = 999; - endptr = &f; + endptr = "somewhere"; str = "0x10000000000000000"; /* 65 bits, 64-bit sign bit clear */ - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); i = 999; - endptr = &f; + endptr = "somewhere"; str = "0x18000000080000000"; /* 65 bits, 64-bit sign bit set */ - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, ULLONG_MAX); g_assert_true(endptr == str + strlen(str)); @@ -219,24 +208,23 @@ static void test_parse_uint_overflow(void) static void test_parse_uint_negative(void) { - unsigned long long i; - char f = 'X'; - char *endptr; + uint64_t i; + const char *endptr; const char *str; int r; i = 999; - endptr = &f; + endptr = "somewhere"; str = " \t -321"; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str + strlen(str)); i = 999; - endptr = &f; + endptr = "somewhere"; str = "-0xffffffff00000001"; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, 0); g_assert_true(endptr == str + strlen(str)); @@ -244,13 +232,12 @@ static void test_parse_uint_negative(void) static void test_parse_uint_negzero(void) { - unsigned long long i = 999; - char f = 'X'; - char *endptr = &f; + uint64_t i = 999; + const char *endptr = "somewhere"; const char *str = " -0"; int r; - r = parse_uint(str, &i, &endptr, 0); + r = parse_uint(str, &endptr, 0, &i); g_assert_cmpint(r, ==, -ERANGE); g_assert_cmpuint(i, ==, 0); @@ -259,11 +246,11 @@ static void test_parse_uint_negzero(void) static void test_parse_uint_full_trailing(void) { - unsigned long long i = 999; + uint64_t i = 999; const char *str = "123xxx"; int r; - r = parse_uint_full(str, &i, 0); + r = parse_uint_full(str, 0, &i); g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); @@ -271,11 +258,11 @@ static void test_parse_uint_full_trailing(void) static void test_parse_uint_full_correct(void) { - unsigned long long i = 999; + uint64_t i = 999; const char *str = "123"; int r; - r = parse_uint_full(str, &i, 0); + r = parse_uint_full(str, 0, &i); g_assert_cmpint(r, ==, 0); g_assert_cmpuint(i, ==, 123); @@ -284,11 +271,11 @@ static void test_parse_uint_full_correct(void) static void test_parse_uint_full_erange_junk(void) { /* FIXME - inconsistent with qemu_strto* which favors EINVAL */ - unsigned long long i = 999; + uint64_t i = 999; const char *str = "-2junk"; int r; - r = parse_uint_full(str, &i, 0); + r = parse_uint_full(str, 0, &i); g_assert_cmpint(r, ==, -ERANGE /* FIXME -EINVAL */); g_assert_cmpuint(i, ==, 0); diff --git a/ui/vnc.c b/ui/vnc.c index 9d8a24dd8a..92964dcc0c 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3728,7 +3728,7 @@ static int vnc_display_get_address(const char *addrstr, } else { const char *port; size_t hostlen; - unsigned long long baseport = 0; + uint64_t baseport = 0; InetSocketAddress *inet; port = strrchr(addrstr, ':'); @@ -3776,7 +3776,7 @@ static int vnc_display_get_address(const char *addrstr, } } else { int offset = reverse ? 0 : 5900; - if (parse_uint_full(port, &baseport, 10) < 0) { + if (parse_uint_full(port, 10, &baseport) < 0) { error_setg(errp, "can't convert to a number: %s", port); goto cleanup; } diff --git a/util/cutils.c b/util/cutils.c index 36c14b769f..0e279a531a 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -722,10 +722,10 @@ const char *qemu_strchrnul(const char *s, int c) * parse_uint: * * @s: String to parse - * @value: Destination for parsed integer value * @endptr: Destination for pointer to first character not consumed, must * not be %NULL * @base: integer base, between 2 and 36 inclusive, or 0 + * @value: Destination for parsed integer value * * Parse unsigned integer * @@ -748,8 +748,7 @@ const char *qemu_strchrnul(const char *s, int c) * * Else, set *@value to the parsed integer, and return 0. */ -int parse_uint(const char *s, unsigned long long *value, char **endptr, - int base) +int parse_uint(const char *s, const char **endptr, int base, uint64_t *value) { int r = 0; char *endp = (char *)s; @@ -793,8 +792,8 @@ out: * parse_uint_full: * * @s: String to parse - * @value: Destination for parsed integer value * @base: integer base, between 2 and 36 inclusive, or 0 + * @value: Destination for parsed integer value * * Parse unsigned integer from entire string * @@ -803,12 +802,12 @@ out: * characters are present after a non-overflowing parsed number, the * function will return -EINVAL, and *@v will be set to 0. */ -int parse_uint_full(const char *s, unsigned long long *value, int base) +int parse_uint_full(const char *s, int base, uint64_t *value) { - char *endp; + const char *endp; int r; - r = parse_uint(s, value, &endp, base); + r = parse_uint(s, &endp, base, value); if (r < 0) { return r; } diff --git a/util/guest-random.c b/util/guest-random.c index a24d27624c..9465dda085 100644 --- a/util/guest-random.c +++ b/util/guest-random.c @@ -89,8 +89,8 @@ void qemu_guest_random_seed_thread_part2(uint64_t seed) int qemu_guest_random_seed_main(const char *optarg, Error **errp) { - unsigned long long seed; - if (parse_uint_full(optarg, &seed, 0)) { + uint64_t seed; + if (parse_uint_full(optarg, 0, &seed)) { error_setg(errp, "Invalid seed number: %s", optarg); return -1; } else { diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index c06a4dce77..892d33f5e6 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -249,12 +249,12 @@ static int inet_listen_saddr(InetSocketAddress *saddr, /* lookup */ if (port_offset) { - unsigned long long baseport; + uint64_t baseport; if (strlen(port) == 0) { error_setg(errp, "port not specified"); return -1; } - if (parse_uint_full(port, &baseport, 10) < 0) { + if (parse_uint_full(port, 10, &baseport) < 0) { error_setg(errp, "can't convert to a number: %s", port); return -1; } @@ -732,19 +732,19 @@ static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr, struct sockaddr_vm *svm, Error **errp) { - unsigned long long val; + uint64_t val; memset(svm, 0, sizeof(*svm)); svm->svm_family = AF_VSOCK; - if (parse_uint_full(vaddr->cid, &val, 10) < 0 || + if (parse_uint_full(vaddr->cid, 10, &val) < 0 || val > UINT32_MAX) { error_setg(errp, "Failed to parse cid '%s'", vaddr->cid); return false; } svm->svm_cid = val; - if (parse_uint_full(vaddr->port, &val, 10) < 0 || + if (parse_uint_full(vaddr->port, 10, &val) < 0 || val > UINT32_MAX) { error_setg(errp, "Failed to parse port '%s'", vaddr->port); return false; From 52d606aa5b6859bf13b026aa82f53f8506aa6abe Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:30 -0500 Subject: [PATCH 10/21] cutils: Allow NULL endptr in parse_uint() All the qemu_strto*() functions permit a NULL endptr, just like their libc counterparts, leaving parse_uint() as the oddball that caused SEGFAULT on NULL and required the user to call parse_uint_full() instead. Relax things for consistency, even though the testsuite is the only impacted caller. Add one more unit test to ensure even parse_uint_full(NULL, 0, &value) works. This also fixes our code to uniformly favor EINVAL over ERANGE when both apply. Also fixes a doc mismatch @v vs. a parameter named value. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-9-eblake@redhat.com> --- tests/unit/test-cutils.c | 18 ++++++++++++++++-- util/cutils.c | 34 ++++++++++++---------------------- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 3664b4fffc..cd8fe7d940 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -270,14 +270,26 @@ static void test_parse_uint_full_correct(void) static void test_parse_uint_full_erange_junk(void) { - /* FIXME - inconsistent with qemu_strto* which favors EINVAL */ + /* EINVAL has priority over ERANGE */ uint64_t i = 999; const char *str = "-2junk"; int r; r = parse_uint_full(str, 0, &i); - g_assert_cmpint(r, ==, -ERANGE /* FIXME -EINVAL */); + g_assert_cmpint(r, ==, -EINVAL); + g_assert_cmpuint(i, ==, 0); +} + +static void test_parse_uint_full_null(void) +{ + uint64_t i = 999; + const char *str = NULL; + int r; + + r = parse_uint_full(str, 0, &i); + + g_assert_cmpint(r, ==, -EINVAL); g_assert_cmpuint(i, ==, 0); } @@ -3328,6 +3340,8 @@ int main(int argc, char **argv) test_parse_uint_full_correct); g_test_add_func("/cutils/parse_uint_full/erange_junk", test_parse_uint_full_erange_junk); + g_test_add_func("/cutils/parse_uint_full/null", + test_parse_uint_full_null); /* qemu_strtoi() tests */ g_test_add_func("/cutils/qemu_strtoi/correct", diff --git a/util/cutils.c b/util/cutils.c index 0e279a531a..56a2aced8d 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -722,8 +722,7 @@ const char *qemu_strchrnul(const char *s, int c) * parse_uint: * * @s: String to parse - * @endptr: Destination for pointer to first character not consumed, must - * not be %NULL + * @endptr: Destination for pointer to first character not consumed * @base: integer base, between 2 and 36 inclusive, or 0 * @value: Destination for parsed integer value * @@ -737,7 +736,8 @@ const char *qemu_strchrnul(const char *s, int c) * * Set *@endptr to point right beyond the parsed integer (even if the integer * overflows or is negative, all digits will be parsed and *@endptr will - * point right beyond them). + * point right beyond them). If @endptr is %NULL, any trailing character + * instead causes a result of -EINVAL with *@value of 0. * * If the integer is negative, set *@value to 0, and return -ERANGE. * (If you want to allow negative numbers that wrap around within @@ -784,7 +784,12 @@ int parse_uint(const char *s, const char **endptr, int base, uint64_t *value) out: *value = val; - *endptr = endp; + if (endptr) { + *endptr = endp; + } else if (s && *endp) { + r = -EINVAL; + *value = 0; + } return r; } @@ -795,28 +800,13 @@ out: * @base: integer base, between 2 and 36 inclusive, or 0 * @value: Destination for parsed integer value * - * Parse unsigned integer from entire string + * Parse unsigned integer from entire string, rejecting any trailing slop. * - * Have the same behavior of parse_uint(), but with an additional - * check for additional data after the parsed number. If extra - * characters are present after a non-overflowing parsed number, the - * function will return -EINVAL, and *@v will be set to 0. + * Shorthand for parse_uint(s, NULL, base, value). */ int parse_uint_full(const char *s, int base, uint64_t *value) { - const char *endp; - int r; - - r = parse_uint(s, &endp, base, value); - if (r < 0) { - return r; - } - if (*endp) { - *value = 0; - return -EINVAL; - } - - return 0; + return parse_uint(s, NULL, base, value); } int qemu_parse_fd(const char *param) From 759573d05b808344f7047f893d2dd095884dfa4d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:31 -0500 Subject: [PATCH 11/21] test-cutils: Add coverage of qemu_strtod It's hard to tweak code for consistency if I can't prove what will or won't break from those tweaks. Time to add unit tests for qemu_strtod() and qemu_strtod_finite(). Among other things, I wrote a check whether we have C99 semantics for strtod("0x1") (which MUST parse hex numbers) rather than C89 (which must stop parsing at 'x'). These days, I suspect that is okay; but if it fails CI checks, knowing the difference will help us decide what we want to do about it. Note that C2x, while not final at the time of this patch, has been considering whether to make strtol("0b1") parse as 1 with no slop instead of the C17 parse of 0 with slop "b1"; that decision may also bleed over to strtod(). But for now, I didn't think it worth adding unit tests on that front (to strtol or strtod) as things may still change. Likewise, there are plenty more corner cases of strtod proper that I don't explicitly test here, but there are enough unit tests added here that it covers all the branches reached in our wrappers. In particular, it demonstrates the difference on when *value is left uninitialized, which an upcoming patch will normalize. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-10-eblake@redhat.com> --- tests/unit/test-cutils.c | 512 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 512 insertions(+) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index cd8fe7d940..0dc9429154 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -25,6 +25,8 @@ * THE SOFTWARE. */ +#include + #include "qemu/osdep.h" #include "qemu/cutils.h" #include "qemu/units.h" @@ -2789,6 +2791,487 @@ static void test_qemu_strtou64_full_erange_junk(void) g_assert_cmpuint(res, ==, UINT64_MAX); } +static void test_qemu_strtod_simple(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* no radix or exponent */ + str = "1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 1); + + /* leading space and sign */ + str = " -0.0"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 5); + + /* fraction only */ + str = "+.5"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 3); + + /* exponent */ + str = "1.e+1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 10.0); + g_assert_true(endptr == str + 5); + + /* hex without radix */ + str = "0x10"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 16.0); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_einval(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* empty */ + str = ""; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); + + /* NULL */ + str = NULL; + endptr = "random"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_null(endptr); + + /* not recognizable */ + str = " junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_erange(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* overflow */ + str = "9e999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, HUGE_VAL); + g_assert_true(endptr == str + 5); + + str = "-9e+999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, -HUGE_VAL); + g_assert_true(endptr == str + 7); + + /* underflow */ + str = "-9e-999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, >=, -DBL_MIN); + g_assert_cmpfloat(res, <=, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 7); +} + +static void test_qemu_strtod_nonfinite(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* infinity */ + str = "inf"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isinf(res)); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 3); + + str = "-infinity"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isinf(res)); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 9); + + /* not a number */ + str = " NaN"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isnan(res)); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_trailing(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* trailing whitespace */ + str = "1. "; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 1.0); + + /* trailing e is not an exponent */ + str = ".5e"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.5); + + /* trailing ( not part of long NaN */ + str = "nan("; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_true(isnan(res)); + g_assert_true(endptr == str + 3); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_true(isnan(res)); +} + +static void test_qemu_strtod_erange_junk(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* ERANGE with trailing junk... */ + str = "1e-999junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, <=, DBL_MIN); + g_assert_cmpfloat(res, >=, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 6); + + /* ...has less priority than EINVAL when full parse not possible */ + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); +} + +static void test_qemu_strtod_finite_simple(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* no radix or exponent */ + str = "1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 1); + + /* leading space and sign */ + str = " -0.0"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 5); + + /* fraction only */ + str = "+.5"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 3); + + /* exponent */ + str = "1.e+1"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 10.0); + g_assert_true(endptr == str + 5); + + /* hex without radix */ + str = "0x10"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 16.0); + g_assert_true(endptr == str + 4); +} + +static void test_qemu_strtod_finite_einval(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* empty */ + str = ""; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); + + /* NULL */ + str = NULL; + endptr = "random"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_null(endptr); + + /* not recognizable */ + str = " junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_finite_erange(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* overflow */ + str = "9e999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, HUGE_VAL); + g_assert_true(endptr == str + 5); + + str = "-9e+999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, ==, -HUGE_VAL); + g_assert_true(endptr == str + 7); + + /* underflow */ + str = "-9e-999"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, >=, -DBL_MIN); + g_assert_cmpfloat(res, <=, -0.0); + g_assert_true(signbit(res)); + g_assert_true(endptr == str + 7); +} + +static void test_qemu_strtod_finite_nonfinite(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* infinity */ + str = "inf"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); + + str = "-infinity"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); + + /* not a number */ + str = " NaN"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); +} + +static void test_qemu_strtod_finite_trailing(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* trailing whitespace */ + str = "1. "; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + + /* trailing e is not an exponent */ + str = ".5e"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpfloat(res, ==, 0.5); + g_assert_true(endptr == str + 2); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + + /* trailing ( not part of long NaN */ + str = "nan("; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); + g_assert_true(endptr == str); + + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); +} + +static void test_qemu_strtod_finite_erange_junk(void) +{ + const char *str; + const char *endptr; + int err; + double res; + + /* ERANGE with trailing junk... */ + str = "1e-999junk"; + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert_cmpfloat(res, <=, DBL_MIN); + g_assert_cmpfloat(res, >=, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str + 6); + + /* ...has less priority than EINVAL when full parse not possible */ + endptr = "somewhere"; + res = 999; + err = qemu_strtod_finite(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 999.0); +} + static void test_qemu_strtosz_simple(void) { const char *str; @@ -3631,6 +4114,35 @@ int main(int argc, char **argv) g_test_add_func("/cutils/qemu_strtou64_full/erange_junk", test_qemu_strtou64_full_erange_junk); + /* qemu_strtod() tests */ + g_test_add_func("/cutils/qemu_strtod/simple", + test_qemu_strtod_simple); + g_test_add_func("/cutils/qemu_strtod/einval", + test_qemu_strtod_einval); + g_test_add_func("/cutils/qemu_strtod/erange", + test_qemu_strtod_erange); + g_test_add_func("/cutils/qemu_strtod/nonfinite", + test_qemu_strtod_nonfinite); + g_test_add_func("/cutils/qemu_strtod/trailing", + test_qemu_strtod_trailing); + g_test_add_func("/cutils/qemu_strtod/erange_junk", + test_qemu_strtod_erange_junk); + + /* qemu_strtod_finite() tests */ + g_test_add_func("/cutils/qemu_strtod_finite/simple", + test_qemu_strtod_finite_simple); + g_test_add_func("/cutils/qemu_strtod_finite/einval", + test_qemu_strtod_finite_einval); + g_test_add_func("/cutils/qemu_strtod_finite/erange", + test_qemu_strtod_finite_erange); + g_test_add_func("/cutils/qemu_strtod_finite/nonfinite", + test_qemu_strtod_finite_nonfinite); + g_test_add_func("/cutils/qemu_strtod_finite/trailing", + test_qemu_strtod_finite_trailing); + g_test_add_func("/cutils/qemu_strtod_finite/erange_junk", + test_qemu_strtod_finite_erange_junk); + + /* qemu_strtosz() tests */ g_test_add_func("/cutils/strtosz/simple", test_qemu_strtosz_simple); g_test_add_func("/cutils/strtosz/hex", From edafce694a53bb5ca3025fce0f3dfe7a40056783 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:32 -0500 Subject: [PATCH 12/21] test-cutils: Prepare for upcoming semantic change in qemu_strtosz A quick search for 'qemu_strtosz' in the code base shows that outside of the testsuite, the ONLY place that passes a non-NULL pointer to @endptr of any variant of a size parser is in hmp.c (the 'o' parser of monitor_parse_arguments), and that particular caller warns of "extraneous characters at the end of line" unless the trailing bytes are purely whitespace. Thus, it makes no semantic difference at the high level whether we parse "1.5e1k" as "1" + ".5e1" + "k" (an attempt to use scientific notation in strtod with a scaling suffix of 'k' with no trailing junk, but which qemu_strtosz says should fail with EINVAL), or as "1.5e" + "1k" (a valid size with scaling suffix of 'e' for exabytes, followed by two junk bytes) - either way, any user passing such a string will get an error message about a parse failure. However, an upcoming patch to qemu_strtosz will fix other corner case bugs in handling the fractional portion of a size, and in doing so, it is easier to declare that qemu_strtosz() itself stops parsing at the first 'e' rather than blindly consuming whatever strtod() will recognize. Once that is fixed, the difference will be visible at the low level (getting a valid parse with trailing garbage when @endptr is non-NULL, while continuing to get -EINVAL when @endptr is NULL); this is easier to demonstrate by moving the affected strings from test_qemu_strtosz_invalid() (which declares them as always -EINVAL) to test_qemu_strtosz_trailing() (where @endptr affects behavior, for now with FIXME comments). Note that a similar argument could be made for having "0x1.5" or "0x1M" parse as 0x1 with ".5" or "M" as trailing junk, instead of blindly treating it as -EINVAL; however, as these cases do not suffer from the same problems as floating point, they are not worth changing at this time. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-11-eblake@redhat.com> --- tests/unit/test-cutils.c | 42 ++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 0dc9429154..8ff291300f 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3563,21 +3563,6 @@ static void test_qemu_strtosz_invalid(void) g_assert_cmphex(res, ==, 0xbaadf00d); g_assert_true(endptr == str); - /* No floating point exponents */ - str = "1.5e1k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "1.5E+0k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - /* No hex fractions */ str = "0x1.8k"; endptr = NULL; @@ -3681,6 +3666,33 @@ static void test_qemu_strtosz_trailing(void) err = qemu_strtosz(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert_cmphex(res, ==, 0xbaadf00d); + + /* FIXME should stop parse after 'e'. No floating point exponents */ + str = "1.5e1k"; + endptr = NULL; + res = 0xbaadf00d; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL /* FIXME 0 */); + g_assert_cmphex(res, ==, 0xbaadf00d /* FIXME EiB * 1.5 */); + g_assert_true(endptr == str /* FIXME + 4 */); + + res = 0xbaadf00d; + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpint(res, ==, 0xbaadf00d); + + str = "1.5E+0k"; + endptr = NULL; + res = 0xbaadf00d; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL /* FIXME 0 */); + g_assert_cmphex(res, ==, 0xbaadf00d /* FIXME EiB * 1.5 */); + g_assert_true(endptr == str /* FIXME + 4 */); + + res = 0xbaadf00d; + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmphex(res, ==, 0xbaadf00d); } static void test_qemu_strtosz_erange(void) From 157367cf21ea01a9413fd6f16808b0bf34804138 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:33 -0500 Subject: [PATCH 13/21] test-cutils: Refactor qemu_strtosz tests for less boilerplate No need to copy-and-paste lots of boilerplate per string tested, when we can consolidate that behind helper functions. Plus, this adds a bit more coverage (we now test all strings both with and without endptr, whereas before some tests skipped the NULL endptr case), which exposed a SEGFAULT on qemu_strtosz(NULL, NULL, &val) that will be fixed in an upcoming patch. Note that duplicating boilerplate has one advantage lost here - a failed test tells you which line number failed; but a helper function does not show the call stack that reached the failure. Since we call the helper more than once within many of the "unit tests", even the unit test name doesn't point out which call is failing. But that only matters when tests fail (they normally pass); at which point I'm debugging the failures under gdb anyways, so I'm not too worried about it. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-12-eblake@redhat.com> --- tests/unit/test-cutils.c | 503 ++++++++------------------------------- 1 file changed, 100 insertions(+), 403 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 8ff291300f..fa94d15891 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3272,473 +3272,170 @@ static void test_qemu_strtod_finite_erange_junk(void) g_assert_cmpfloat(res, ==, 999.0); } +typedef int (*qemu_strtosz_fn)(const char *, const char **, uint64_t *); +static void do_strtosz_full(const char *str, qemu_strtosz_fn fn, + int exp_ptr_ret, uint64_t exp_ptr_val, + size_t exp_ptr_offset, int exp_null_ret, + uint64_t exp_null_val) +{ + const char *endptr = "somewhere"; + uint64_t val = 0xbaadf00d; + int ret; + + ret = fn(str, &endptr, &val); + g_assert_cmpint(ret, ==, exp_ptr_ret); + g_assert_cmpuint(val, ==, exp_ptr_val); + g_assert_true(endptr == str + exp_ptr_offset); + + val = 0xbaadf00d; + ret = fn(str, NULL, &val); + g_assert_cmpint(ret, ==, exp_null_ret); + g_assert_cmpuint(val, ==, exp_null_val); +} + +static void do_strtosz(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + +static void do_strtosz_MiB(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz_MiB, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + +static void do_strtosz_metric(const char *str, int exp_ret, uint64_t exp_val, + size_t exp_offset) +{ + do_strtosz_full(str, qemu_strtosz_metric, exp_ret, exp_val, exp_offset, + exp_ret, exp_val); +} + static void test_qemu_strtosz_simple(void) { - const char *str; - const char *endptr; - int err; - uint64_t res; - - str = "0"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0); - g_assert_true(endptr == str + 1); + do_strtosz("0", 0, 0, 1); /* Leading 0 gives decimal results, not octal */ - str = "08"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 8); - g_assert_true(endptr == str + 2); + do_strtosz("08", 0, 8, 2); /* Leading space is ignored */ - str = " 12345"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345); - g_assert_true(endptr == str + 6); + do_strtosz(" 12345", 0, 12345, 6); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345); + /* 2^53-1 */ + do_strtosz("9007199254740991", 0, 0x1fffffffffffffULL, 16); - str = "9007199254740991"; /* 2^53-1 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x1fffffffffffffULL); - g_assert_true(endptr == str + 16); + /* 2^53 */ + do_strtosz("9007199254740992", 0, 0x20000000000000ULL, 16); - str = "9007199254740992"; /* 2^53 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x20000000000000ULL); - g_assert_true(endptr == str + 16); + /* 2^53+1 */ + do_strtosz("9007199254740993", 0, 0x20000000000001ULL, 16); - str = "9007199254740993"; /* 2^53+1 */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0x20000000000001ULL); - g_assert_true(endptr == str + 16); + /* 0xfffffffffffff800 (53 msbs set) */ + do_strtosz("18446744073709549568", 0, 0xfffffffffffff800ULL, 20); - str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0xfffffffffffff800ULL); - g_assert_true(endptr == str + 20); + /* 0xfffffffffffffbff */ + do_strtosz("18446744073709550591", 0, 0xfffffffffffffbffULL, 20); - str = "18446744073709550591"; /* 0xfffffffffffffbff */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0xfffffffffffffbffULL); - g_assert_true(endptr == str + 20); - - str = "18446744073709551615"; /* 0xffffffffffffffff */ - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmphex(res, ==, 0xffffffffffffffffULL); - g_assert_true(endptr == str + 20); + /* 0xffffffffffffffff */ + do_strtosz("18446744073709551615", 0, 0xffffffffffffffffULL, 20); } static void test_qemu_strtosz_hex(void) { - const char *str; - const char *endptr; - int err; - uint64_t res; + do_strtosz("0x0", 0, 0, 3); - str = "0x0"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0); - g_assert_true(endptr == str + 3); + do_strtosz("0xab", 0, 171, 4); - str = "0xab"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 171); - g_assert_true(endptr == str + 4); - - str = "0xae"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 174); - g_assert_true(endptr == str + 4); + do_strtosz("0xae", 0, 174, 4); } static void test_qemu_strtosz_units(void) { - const char *none = "1"; - const char *b = "1B"; - const char *k = "1K"; - const char *m = "1M"; - const char *g = "1G"; - const char *t = "1T"; - const char *p = "1P"; - const char *e = "1E"; - int err; - const char *endptr; - uint64_t res; - /* default is M */ - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz_MiB(none, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, MiB); - g_assert_true(endptr == none + 1); + do_strtosz_MiB("1", 0, MiB, 1); - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(b, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 1); - g_assert_true(endptr == b + 2); + do_strtosz("1B", 0, 1, 2); - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(k, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, KiB); - g_assert_true(endptr == k + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(m, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, MiB); - g_assert_true(endptr == m + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(g, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, GiB); - g_assert_true(endptr == g + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(t, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, TiB); - g_assert_true(endptr == t + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(p, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, PiB); - g_assert_true(endptr == p + 2); - - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(e, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, EiB); - g_assert_true(endptr == e + 2); + do_strtosz("1K", 0, KiB, 2); + do_strtosz("1M", 0, MiB, 2); + do_strtosz("1G", 0, GiB, 2); + do_strtosz("1T", 0, TiB, 2); + do_strtosz("1P", 0, PiB, 2); + do_strtosz("1E", 0, EiB, 2); } static void test_qemu_strtosz_float(void) { - const char *str; - int err; - const char *endptr; - uint64_t res; - - str = "0.5E"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, EiB / 2); - g_assert_true(endptr == str + 4); + do_strtosz("0.5E", 0, EiB / 2, 4); /* For convenience, a fraction of 0 is tolerated even on bytes */ - str = "1.0B"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 1); - g_assert_true(endptr == str + 4); + do_strtosz("1.0B", 0, 1, 4); /* An empty fraction is tolerated */ - str = "1.k"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 1024); - g_assert_true(endptr == str + 3); + do_strtosz("1.k", 0, 1024, 3); /* For convenience, we permit values that are not byte-exact */ - str = "12.345M"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, (uint64_t) (12.345 * MiB + 0.5)); - g_assert_true(endptr == str + 7); + do_strtosz("12.345M", 0, (uint64_t) (12.345 * MiB + 0.5), 7); } static void test_qemu_strtosz_invalid(void) { - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; - - str = ""; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = " \t "; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "crap"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "inf"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "NaN"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("", -EINVAL, 0xbaadf00d, 0); + do_strtosz(" \t ", -EINVAL, 0xbaadf00d, 0); + do_strtosz("crap", -EINVAL, 0xbaadf00d, 0); + do_strtosz("inf", -EINVAL, 0xbaadf00d, 0); + do_strtosz("NaN", -EINVAL, 0xbaadf00d, 0); /* Fractional values require scale larger than bytes */ - str = "1.1B"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "1.1"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("1.1B", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.1", -EINVAL, 0xbaadf00d, 0); /* No hex fractions */ - str = "0x1.8k"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("0x1.8k", -EINVAL, 0xbaadf00d, 0); /* No suffixes */ - str = "0x18M"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("0x18M", -EINVAL, 0xbaadf00d, 0); /* No negative values */ - str = "-0"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); - - str = "-1"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str); + do_strtosz("-0", -EINVAL, 0xbaadf00d, 0); + do_strtosz("-1", -EINVAL, 0xbaadf00d, 0); } static void test_qemu_strtosz_trailing(void) { - const char *str; - const char *endptr; - int err; - uint64_t res; + do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, + -EINVAL, 0xbaadf00d); - str = "123xxx"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz_MiB(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123 * MiB); - g_assert_true(endptr == str + 3); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - - str = "1kiB"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 1024); - g_assert_true(endptr == str + 2); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - - str = "0x"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0); - g_assert_true(endptr == str + 1); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - - str = "0.NaN"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 0); - g_assert_true(endptr == str + 2); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); - - str = "123-45"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 123); - g_assert_true(endptr == str + 3); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); + do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0xbaadf00d); + do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); /* FIXME should stop parse after 'e'. No floating point exponents */ - str = "1.5e1k"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL /* FIXME 0 */); - g_assert_cmphex(res, ==, 0xbaadf00d /* FIXME EiB * 1.5 */); - g_assert_true(endptr == str /* FIXME + 4 */); + do_strtosz_full("1.5e1k", qemu_strtosz, -EINVAL /* FIXME 0 */, + 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, + -EINVAL, 0xbaadf00d); - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 0xbaadf00d); - - str = "1.5E+0k"; - endptr = NULL; - res = 0xbaadf00d; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -EINVAL /* FIXME 0 */); - g_assert_cmphex(res, ==, 0xbaadf00d /* FIXME EiB * 1.5 */); - g_assert_true(endptr == str /* FIXME + 4 */); - - res = 0xbaadf00d; - err = qemu_strtosz(str, NULL, &res); - g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmphex(res, ==, 0xbaadf00d); + do_strtosz_full("1.5E+0k", qemu_strtosz, -EINVAL /* FIXME 0 */, + 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, + -EINVAL, 0xbaadf00d); } static void test_qemu_strtosz_erange(void) { - const char *str; - const char *endptr; - int err; - uint64_t res = 0xbaadf00d; + /* 2^64; see strtosz_simple for 2^64-1 */ + do_strtosz("18446744073709551616", -ERANGE, 0xbaadf00d, 20); - str = "18446744073709551616"; /* 2^64; see strtosz_simple for 2^64-1 */ - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str + 20); - - str = "20E"; - endptr = NULL; - err = qemu_strtosz(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmphex(res, ==, 0xbaadf00d); - g_assert_true(endptr == str + 3); + do_strtosz("20E", -ERANGE, 0xbaadf00d, 3); } static void test_qemu_strtosz_metric(void) { - const char *str; - int err; - const char *endptr; - uint64_t res; - - str = "12345k"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz_metric(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345000); - g_assert_true(endptr == str + 6); - - str = "12.345M"; - endptr = str; - res = 0xbaadf00d; - err = qemu_strtosz_metric(str, &endptr, &res); - g_assert_cmpint(err, ==, 0); - g_assert_cmpuint(res, ==, 12345000); - g_assert_true(endptr == str + 7); + do_strtosz_metric("12345k", 0, 12345000, 6); + do_strtosz_metric("12.345M", 0, 12345000, 7); } static void test_freq_to_str(void) From f49371ecae182eb07cfb105aa7ea637479d83764 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:34 -0500 Subject: [PATCH 14/21] cutils: Allow NULL str in qemu_strtosz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All the other qemu_strto* and parse_uint allow a NULL str. Having qemu_strtosz not crash on qemu_strtosz(NULL, NULL, &value) is an easy fix that adds some consistency between our string parsers. Signed-off-by: Eric Blake Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-13-eblake@redhat.com> --- tests/unit/test-cutils.c | 10 +++++++++- util/cutils.c | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index fa94d15891..095a611d1e 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3285,7 +3285,12 @@ static void do_strtosz_full(const char *str, qemu_strtosz_fn fn, ret = fn(str, &endptr, &val); g_assert_cmpint(ret, ==, exp_ptr_ret); g_assert_cmpuint(val, ==, exp_ptr_val); - g_assert_true(endptr == str + exp_ptr_offset); + if (str) { + g_assert_true(endptr == str + exp_ptr_offset); + } else { + g_assert_cmpint(exp_ptr_offset, ==, 0); + g_assert_null(endptr); + } val = 0xbaadf00d; ret = fn(str, NULL, &val); @@ -3383,6 +3388,9 @@ static void test_qemu_strtosz_float(void) static void test_qemu_strtosz_invalid(void) { + do_strtosz(NULL, -EINVAL, 0xbaadf00d, 0); + + /* Must parse at least one digit */ do_strtosz("", -EINVAL, 0xbaadf00d, 0); do_strtosz(" \t ", -EINVAL, 0xbaadf00d, 0); do_strtosz("crap", -EINVAL, 0xbaadf00d, 0); diff --git a/util/cutils.c b/util/cutils.c index 56a2aced8d..1dc67d201d 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -306,7 +306,7 @@ static int do_strtosz(const char *nptr, const char **end, out: if (end) { *end = endptr; - } else if (*endptr) { + } else if (nptr && *endptr) { retval = -EINVAL; } if (retval == 0) { From a73049b2a1bad81c1059b79cf4567e4d6932634f Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:35 -0500 Subject: [PATCH 15/21] numa: Check for qemu_strtosz_MiB error As shown in the previous commit, qemu_strtosz_MiB sometimes leaves the result value untouched (we have to audit further to learn that in that case, the QAPI generator says that visit_type_NumaOptions() will have zero-initialized it), and sometimes leaves it with the value of a partial parse before -EINVAL occurs because of trailing garbage. Rather than blindly treating any string the user may throw at us as valid, we should check for parse failures. Fixes: cc001888 ("numa: fixup parsed NumaNodeOptions earlier", v2.11.0) Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-14-eblake@redhat.com> --- hw/core/numa.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/core/numa.c b/hw/core/numa.c index d8d36b16d8..f08956ddb0 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -531,10 +531,17 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) /* Fix up legacy suffix-less format */ if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { const char *mem_str = qemu_opt_get(opts, "mem"); - qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + int ret = qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + + if (ret < 0) { + error_setg_errno(&err, -ret, "could not parse memory size '%s'", + mem_str); + } } - set_numa_options(ms, object, &err); + if (!err) { + set_numa_options(ms, object, &err); + } qapi_free_NumaOptions(object); if (err) { From e1cf34b6b33caea5e47d46ba715eca6603fdbbc4 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:36 -0500 Subject: [PATCH 16/21] test-cutils: Add more coverage to qemu_strtosz Add some more strings that the user might send our way. In particular, some of these additions include FIXME comments showing where our parser doesn't quite behave the way we want. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-15-eblake@redhat.com> --- tests/unit/test-cutils.c | 140 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 129 insertions(+), 11 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 095a611d1e..8ea6065855 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3326,8 +3326,8 @@ static void test_qemu_strtosz_simple(void) /* Leading 0 gives decimal results, not octal */ do_strtosz("08", 0, 8, 2); - /* Leading space is ignored */ - do_strtosz(" 12345", 0, 12345, 6); + /* Leading space and + are ignored */ + do_strtosz(" +12345", 0, 12345, 7); /* 2^53-1 */ do_strtosz("9007199254740991", 0, 0x1fffffffffffffULL, 16); @@ -3354,17 +3354,27 @@ static void test_qemu_strtosz_hex(void) do_strtosz("0xab", 0, 171, 4); - do_strtosz("0xae", 0, 174, 4); + do_strtosz(" +0xae", 0, 174, 6); } static void test_qemu_strtosz_units(void) { - /* default is M */ + /* default scale depends on function */ + do_strtosz("1", 0, 1, 1); do_strtosz_MiB("1", 0, MiB, 1); + do_strtosz_metric("1", 0, 1, 1); + /* Explicit byte suffix works for all functions */ do_strtosz("1B", 0, 1, 2); + do_strtosz_MiB("1B", 0, 1, 2); + do_strtosz_metric("1B", 0, 1, 2); + /* Expose the scale */ do_strtosz("1K", 0, KiB, 2); + do_strtosz_MiB("1K", 0, KiB, 2); + do_strtosz_metric("1K", 0, 1000, 2); + + /* Other suffixes, see also test_qemu_strtosz_metric */ do_strtosz("1M", 0, MiB, 2); do_strtosz("1G", 0, GiB, 2); do_strtosz("1T", 0, TiB, 2); @@ -3376,14 +3386,37 @@ static void test_qemu_strtosz_float(void) { do_strtosz("0.5E", 0, EiB / 2, 4); + /* Implied M suffix okay */ + do_strtosz_MiB("0.5", 0, MiB / 2, 3); + /* For convenience, a fraction of 0 is tolerated even on bytes */ do_strtosz("1.0B", 0, 1, 4); - /* An empty fraction is tolerated */ + /* An empty fraction tail is tolerated */ do_strtosz("1.k", 0, 1024, 3); + /* FIXME An empty fraction head should be tolerated */ + do_strtosz(" .5k", -EINVAL /* FIXME 0 */, 0xbaadf00d /* FIXME 512 */, + 0 /* FIXME 4 */); + /* For convenience, we permit values that are not byte-exact */ do_strtosz("12.345M", 0, (uint64_t) (12.345 * MiB + 0.5), 7); + + /* FIXME Fraction tail should round correctly */ + do_strtosz("1.9999k", 0, 2048, 7); + do_strtosz("1.9999999999999999999999999999999999999999999999999999k", 0, + 1024 /* FIXME 2048 */, 55); + + /* FIXME ERANGE underflow in the fraction tail should not matter for 'k' */ + do_strtosz("1." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1k", 0, 1 /* FIXME 1024 */, 354); } static void test_qemu_strtosz_invalid(void) @@ -3393,57 +3426,142 @@ static void test_qemu_strtosz_invalid(void) /* Must parse at least one digit */ do_strtosz("", -EINVAL, 0xbaadf00d, 0); do_strtosz(" \t ", -EINVAL, 0xbaadf00d, 0); - do_strtosz("crap", -EINVAL, 0xbaadf00d, 0); + do_strtosz(".", -EINVAL, 0xbaadf00d, 0); + do_strtosz(" .", -EINVAL, 0xbaadf00d, 0); + do_strtosz(" .k", -EINVAL, 0xbaadf00d, 0); do_strtosz("inf", -EINVAL, 0xbaadf00d, 0); do_strtosz("NaN", -EINVAL, 0xbaadf00d, 0); + /* Lone suffix is not okay */ + do_strtosz("k", -EINVAL, 0xbaadf00d, 0); + do_strtosz(" M", -EINVAL, 0xbaadf00d, 0); + /* Fractional values require scale larger than bytes */ do_strtosz("1.1B", -EINVAL, 0xbaadf00d, 0); do_strtosz("1.1", -EINVAL, 0xbaadf00d, 0); + /* FIXME underflow in the fraction tail should matter for 'B' */ + do_strtosz("1.00001B", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.00000000000000000001B", 0 /* FIXME -EINVAL */, + 1 /* FIXME 0xbaadf00d */, 23 /* FIXME 0 */); + do_strtosz("1." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1B", 0 /* FIXME -EINVAL */, 1 /* FIXME 0xbaadf00d */, + 354 /* FIXME 0 */); + /* No hex fractions */ do_strtosz("0x1.8k", -EINVAL, 0xbaadf00d, 0); + do_strtosz("0x1.k", -EINVAL, 0xbaadf00d, 0); - /* No suffixes */ + /* No hex suffixes */ do_strtosz("0x18M", -EINVAL, 0xbaadf00d, 0); + do_strtosz("0x1p1", -EINVAL, 0xbaadf00d, 0); - /* No negative values */ - do_strtosz("-0", -EINVAL, 0xbaadf00d, 0); - do_strtosz("-1", -EINVAL, 0xbaadf00d, 0); + /* decimal in place of scaling suffix */ + do_strtosz("1.1.k", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.1.", -EINVAL, 0xbaadf00d, 0); } static void test_qemu_strtosz_trailing(void) { + /* Trailing whitespace */ + do_strtosz_full("1k ", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + + /* Unknown suffix overrides even implied scale*/ + do_strtosz_full("123xxx", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); + + /* Implied scale allows partial parse */ do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, -EINVAL, 0xbaadf00d); + do_strtosz_full("1.5.k", qemu_strtosz_MiB, 0, 1.5 * MiB, 3, + -EINVAL, 0xbaadf00d); + /* Junk after one-byte suffix */ do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + + /* Incomplete hex is an unknown suffix */ do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0xbaadf00d); + + /* Hex literals use only one leading zero */ + do_strtosz_full("00x1", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + + /* No support for binary literals; 'b' is valid suffix */ + do_strtosz_full("0b1000", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + + /* Junk after decimal */ do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + + /* Although negatives are invalid, '-' may be in trailing junk */ do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); + do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0xbaadf00d); /* FIXME should stop parse after 'e'. No floating point exponents */ do_strtosz_full("1.5e1k", qemu_strtosz, -EINVAL /* FIXME 0 */, 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, -EINVAL, 0xbaadf00d); - do_strtosz_full("1.5E+0k", qemu_strtosz, -EINVAL /* FIXME 0 */, 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, -EINVAL, 0xbaadf00d); + + /* + * FIXME overflow in fraction is so buggy it can read beyond bounds + * if we don't stuff extra \0 in our literal + */ + do_strtosz_full("1.5E999\0\0" /* FIXME 1.5E999" */, qemu_strtosz, + 0, 1 /* FIXME EiB * 1.5 */, 8 /* FIXME 4 */, + 0 /* FIXME -EINVAL */, 1 /* FIXME 0xbaadf00d */); } static void test_qemu_strtosz_erange(void) { + /* FIXME negative values fit better as ERANGE */ + do_strtosz(" -0", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, 0 /* FIXME 3 */); + do_strtosz("-1", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, 0 /* FIXME 2 */); + do_strtosz_full("-2M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, + 0xbaadf00d, 0 /* FIXME 2 */, -EINVAL, 0xbaadf00d); + do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, + 0 /* FIXME 4 */); + do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, + 0xbaadf00d, 0 /* FIXME 3 */, -EINVAL, 0xbaadf00d); + do_strtosz_full(" -." + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000" + "1M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, + 0xbaadf00d, 0 /* FIXME 354 */, -EINVAL, 0xbaadf00d); + /* 2^64; see strtosz_simple for 2^64-1 */ do_strtosz("18446744073709551616", -ERANGE, 0xbaadf00d, 20); do_strtosz("20E", -ERANGE, 0xbaadf00d, 3); + + /* FIXME Fraction tail can cause ERANGE overflow */ + do_strtosz("15.9999999999999999999999999999999999999999999999999999E", + 0 /* FIXME -ERANGE */, 15ULL * EiB /* FIXME 0xbaadf00d */, 56); + + /* EINVAL has priority over ERANGE */ + do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0xbaadf00d, 7, + -EINVAL, 0xbaadf00d); } static void test_qemu_strtosz_metric(void) { do_strtosz_metric("12345k", 0, 12345000, 6); do_strtosz_metric("12.345M", 0, 12345000, 7); + + /* Fraction is affected by floating-point rounding */ + /* This would be 0xfffffffffffffbff with infinite precision */ + do_strtosz_metric("18.446744073709550591E", 0, 0xfffffffffffffc0cULL, 22); } static void test_freq_to_str(void) From 896fbd90aa3ffd4ad57b35722128fe65a9abc530 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:37 -0500 Subject: [PATCH 17/21] cutils: Set value in all qemu_strtosz* error paths Making callers determine whether or not *value was populated on error is not nice for usability. Pre-patch, we have unit tests that check that *result is left unchanged on most EINVAL errors and set to 0 on many ERANGE errors. This is subtly different from libc strtoumax() behavior which returns UINT64_MAX on ERANGE errors, as well as different from our parse_uint() which slams to 0 on EINVAL on the grounds that we want our functions to be harder to mis-use than strtoumax(). Let's audit callers: - hw/core/numa.c:parse_numa() fixed in the previous patch to check for errors - migration/migration-hmp-cmds.c:hmp_migrate_set_parameter(), monitor/hmp.c:monitor_parse_arguments(), qapi/opts-visitor.c:opts_type_size(), qapi/qobject-input-visitor.c:qobject_input_type_size_keyval(), qemu-img.c:cvtnum_full(), qemu-io-cmds.c:cvtnum(), target/i386/cpu.c:x86_cpu_parse_featurestr(), and util/qemu-option.c:parse_option_size() appear to reject all failures (although some with distinct messages for ERANGE as opposed to EINVAL), so it doesn't matter what is in the value parameter on error. - All remaining callers are in the testsuite, where we can tweak our expectations to match our new desired behavior. Advancing to the end of the string parsed on overflow (ERANGE), while still returning 0, makes sense (UINT64_MAX as a size is unlikely to be useful); likewise, our size parsing code is complex enough that it's easier to always return 0 when endptr is NULL but trailing garbage was found, rather than trying to return the value of the prefix actually parsed (no current caller cared about the value of the prefix). Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-16-eblake@redhat.com> --- tests/unit/test-cutils.c | 106 +++++++++++++++++++-------------------- util/cutils.c | 17 +++++-- 2 files changed, 63 insertions(+), 60 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 8ea6065855..f4dfb6ae14 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3396,7 +3396,7 @@ static void test_qemu_strtosz_float(void) do_strtosz("1.k", 0, 1024, 3); /* FIXME An empty fraction head should be tolerated */ - do_strtosz(" .5k", -EINVAL /* FIXME 0 */, 0xbaadf00d /* FIXME 512 */, + do_strtosz(" .5k", -EINVAL /* FIXME 0 */, 0 /* FIXME 512 */, 0 /* FIXME 4 */); /* For convenience, we permit values that are not byte-exact */ @@ -3421,29 +3421,29 @@ static void test_qemu_strtosz_float(void) static void test_qemu_strtosz_invalid(void) { - do_strtosz(NULL, -EINVAL, 0xbaadf00d, 0); + do_strtosz(NULL, -EINVAL, 0, 0); /* Must parse at least one digit */ - do_strtosz("", -EINVAL, 0xbaadf00d, 0); - do_strtosz(" \t ", -EINVAL, 0xbaadf00d, 0); - do_strtosz(".", -EINVAL, 0xbaadf00d, 0); - do_strtosz(" .", -EINVAL, 0xbaadf00d, 0); - do_strtosz(" .k", -EINVAL, 0xbaadf00d, 0); - do_strtosz("inf", -EINVAL, 0xbaadf00d, 0); - do_strtosz("NaN", -EINVAL, 0xbaadf00d, 0); + do_strtosz("", -EINVAL, 0, 0); + do_strtosz(" \t ", -EINVAL, 0, 0); + do_strtosz(".", -EINVAL, 0, 0); + do_strtosz(" .", -EINVAL, 0, 0); + do_strtosz(" .k", -EINVAL, 0, 0); + do_strtosz("inf", -EINVAL, 0, 0); + do_strtosz("NaN", -EINVAL, 0, 0); /* Lone suffix is not okay */ - do_strtosz("k", -EINVAL, 0xbaadf00d, 0); - do_strtosz(" M", -EINVAL, 0xbaadf00d, 0); + do_strtosz("k", -EINVAL, 0, 0); + do_strtosz(" M", -EINVAL, 0, 0); /* Fractional values require scale larger than bytes */ - do_strtosz("1.1B", -EINVAL, 0xbaadf00d, 0); - do_strtosz("1.1", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.1B", -EINVAL, 0, 0); + do_strtosz("1.1", -EINVAL, 0, 0); /* FIXME underflow in the fraction tail should matter for 'B' */ - do_strtosz("1.00001B", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.00001B", -EINVAL, 0, 0); do_strtosz("1.00000000000000000001B", 0 /* FIXME -EINVAL */, - 1 /* FIXME 0xbaadf00d */, 23 /* FIXME 0 */); + 1 /* FIXME 0 */, 23 /* FIXME 0 */); do_strtosz("1." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3452,62 +3452,60 @@ static void test_qemu_strtosz_invalid(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1B", 0 /* FIXME -EINVAL */, 1 /* FIXME 0xbaadf00d */, + "1B", 0 /* FIXME -EINVAL */, 1 /* FIXME 0 */, 354 /* FIXME 0 */); /* No hex fractions */ - do_strtosz("0x1.8k", -EINVAL, 0xbaadf00d, 0); - do_strtosz("0x1.k", -EINVAL, 0xbaadf00d, 0); + do_strtosz("0x1.8k", -EINVAL, 0, 0); + do_strtosz("0x1.k", -EINVAL, 0, 0); /* No hex suffixes */ - do_strtosz("0x18M", -EINVAL, 0xbaadf00d, 0); - do_strtosz("0x1p1", -EINVAL, 0xbaadf00d, 0); + do_strtosz("0x18M", -EINVAL, 0, 0); + do_strtosz("0x1p1", -EINVAL, 0, 0); /* decimal in place of scaling suffix */ - do_strtosz("1.1.k", -EINVAL, 0xbaadf00d, 0); - do_strtosz("1.1.", -EINVAL, 0xbaadf00d, 0); + do_strtosz("1.1.k", -EINVAL, 0, 0); + do_strtosz("1.1.", -EINVAL, 0, 0); } static void test_qemu_strtosz_trailing(void) { /* Trailing whitespace */ - do_strtosz_full("1k ", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("1k ", qemu_strtosz, 0, 1024, 2, -EINVAL, 0); /* Unknown suffix overrides even implied scale*/ - do_strtosz_full("123xxx", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); + do_strtosz_full("123xxx", qemu_strtosz, 0, 123, 3, -EINVAL, 0); /* Implied scale allows partial parse */ - do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, - -EINVAL, 0xbaadf00d); - do_strtosz_full("1.5.k", qemu_strtosz_MiB, 0, 1.5 * MiB, 3, - -EINVAL, 0xbaadf00d); + do_strtosz_full("123xxx", qemu_strtosz_MiB, 0, 123 * MiB, 3, -EINVAL, 0); + do_strtosz_full("1.5.k", qemu_strtosz_MiB, 0, 1.5 * MiB, 3, -EINVAL, 0); /* Junk after one-byte suffix */ - do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("1kiB", qemu_strtosz, 0, 1024, 2, -EINVAL, 0); /* Incomplete hex is an unknown suffix */ - do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0xbaadf00d); + do_strtosz_full("0x", qemu_strtosz, 0, 0, 1, -EINVAL, 0); /* Hex literals use only one leading zero */ - do_strtosz_full("00x1", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("00x1", qemu_strtosz, 0, 0, 2, -EINVAL, 0); /* No support for binary literals; 'b' is valid suffix */ - do_strtosz_full("0b1000", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("0b1000", qemu_strtosz, 0, 0, 2, -EINVAL, 0); /* Junk after decimal */ - do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0xbaadf00d); + do_strtosz_full("0.NaN", qemu_strtosz, 0, 0, 2, -EINVAL, 0); /* Although negatives are invalid, '-' may be in trailing junk */ - do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0xbaadf00d); - do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0xbaadf00d); + do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0); + do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0); /* FIXME should stop parse after 'e'. No floating point exponents */ do_strtosz_full("1.5e1k", qemu_strtosz, -EINVAL /* FIXME 0 */, - 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, - -EINVAL, 0xbaadf00d); + 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, + -EINVAL, 0); do_strtosz_full("1.5E+0k", qemu_strtosz, -EINVAL /* FIXME 0 */, - 0xbaadf00d /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, - -EINVAL, 0xbaadf00d); + 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, + -EINVAL, 0); /* * FIXME overflow in fraction is so buggy it can read beyond bounds @@ -3515,20 +3513,19 @@ static void test_qemu_strtosz_trailing(void) */ do_strtosz_full("1.5E999\0\0" /* FIXME 1.5E999" */, qemu_strtosz, 0, 1 /* FIXME EiB * 1.5 */, 8 /* FIXME 4 */, - 0 /* FIXME -EINVAL */, 1 /* FIXME 0xbaadf00d */); + 0 /* FIXME -EINVAL */, 1 /* FIXME 0 */); } static void test_qemu_strtosz_erange(void) { /* FIXME negative values fit better as ERANGE */ - do_strtosz(" -0", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, 0 /* FIXME 3 */); - do_strtosz("-1", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, 0 /* FIXME 2 */); - do_strtosz_full("-2M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, - 0xbaadf00d, 0 /* FIXME 2 */, -EINVAL, 0xbaadf00d); - do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0xbaadf00d, - 0 /* FIXME 4 */); - do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, - 0xbaadf00d, 0 /* FIXME 3 */, -EINVAL, 0xbaadf00d); + do_strtosz(" -0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 3 */); + do_strtosz("-1", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 2 */); + do_strtosz_full("-2M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, + 0 /* FIXME 2 */, -EINVAL, 0); + do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 4 */); + do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, + 0 /* FIXME 3 */, -EINVAL, 0); do_strtosz_full(" -." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3537,21 +3534,20 @@ static void test_qemu_strtosz_erange(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, - 0xbaadf00d, 0 /* FIXME 354 */, -EINVAL, 0xbaadf00d); + "1M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, + 0 /* FIXME 354 */, -EINVAL, 0); /* 2^64; see strtosz_simple for 2^64-1 */ - do_strtosz("18446744073709551616", -ERANGE, 0xbaadf00d, 20); + do_strtosz("18446744073709551616", -ERANGE, 0, 20); - do_strtosz("20E", -ERANGE, 0xbaadf00d, 3); + do_strtosz("20E", -ERANGE, 0, 3); /* FIXME Fraction tail can cause ERANGE overflow */ do_strtosz("15.9999999999999999999999999999999999999999999999999999E", - 0 /* FIXME -ERANGE */, 15ULL * EiB /* FIXME 0xbaadf00d */, 56); + 0 /* FIXME -ERANGE */, 15ULL * EiB /* FIXME 0 */, 56); /* EINVAL has priority over ERANGE */ - do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0xbaadf00d, 7, - -EINVAL, 0xbaadf00d); + do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0, 7, -EINVAL, 0); } static void test_qemu_strtosz_metric(void) diff --git a/util/cutils.c b/util/cutils.c index 1dc67d201d..c5530a5c2b 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -205,13 +205,15 @@ static int64_t suffix_mul(char suffix, int64_t unit) * * The end pointer will be returned in *end, if not NULL. If there is * no fraction, the input can be decimal or hexadecimal; if there is a - * fraction, then the input must be decimal and there must be a suffix - * (possibly by @default_suffix) larger than Byte, and the fractional - * portion may suffer from precision loss or rounding. The input must - * be positive. + * non-zero fraction, then the input must be decimal and there must be + * a suffix (possibly by @default_suffix) larger than Byte, and the + * fractional portion may suffer from precision loss or rounding. The + * input must be positive. * * Return -ERANGE on overflow (with *@end advanced), and -EINVAL on - * other error (with *@end left unchanged). + * other error (with *@end at @nptr). Unlike strtoull, *@result is + * set to 0 on all errors, as returning UINT64_MAX on overflow is less + * likely to be usable as a size. */ static int do_strtosz(const char *nptr, const char **end, const char default_suffix, int64_t unit, @@ -311,6 +313,11 @@ out: } if (retval == 0) { *result = val; + } else { + *result = 0; + if (end && retval == -EINVAL) { + *end = nptr; + } } return retval; From 3c5f2467984c23aae5a64548dcb15efae18e207e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:38 -0500 Subject: [PATCH 18/21] cutils: Set value in all integral qemu_strto* error paths Our goal in writing qemu_strtoi() and friends is to have an interface harder to abuse than libc's strtol(). Leaving the return value uninitialized on some but not all error paths does not lend itself well to this goal; and our documentation wasn't helpful on what to expect. Note that the previous patch changed all qemu_strtosz() EINVAL error paths to slam value to 0 rather than stay uninitialized, even when the EINVAL eror occurs because of trailing junk. But for the remaining integral qemu_strto*, it's easier to return the parsed value than to force things back to zero, in part because of how check_strtox_error works; in part because people expect that from libc strto* (while there is no libc strtosz to compare to), and in part because doing so creates less churn in the testsuite. Here, the list of affected callers is much longer ('git grep "qemu_strto[ui]" "*.c" "**/*.c" | grep -v tests/ |wc -l' outputs 107, although a few of those are the implementation in in cutils.c), so touching as little as possible is the wisest course of action. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-17-eblake@redhat.com> --- tests/unit/test-cutils.c | 24 +++++++++++------------ util/cutils.c | 42 +++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index f4dfb6ae14..cfe1fc8a17 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -320,7 +320,7 @@ static void test_qemu_strtoi_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -661,7 +661,7 @@ static void test_qemu_strtoi_full_null(void) err = qemu_strtoi(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -764,7 +764,7 @@ static void test_qemu_strtoui_null(void) err = qemu_strtoui(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); g_assert_null(endptr); } @@ -1102,7 +1102,7 @@ static void test_qemu_strtoui_full_null(void) err = qemu_strtoui(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtoui_full_empty(void) @@ -1202,7 +1202,7 @@ static void test_qemu_strtol_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -1516,7 +1516,7 @@ static void test_qemu_strtol_full_null(void) err = qemu_strtol(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -1619,7 +1619,7 @@ static void test_qemu_strtoul_null(void) err = qemu_strtoul(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); g_assert_null(endptr); } @@ -1932,7 +1932,7 @@ static void test_qemu_strtoul_full_null(void) err = qemu_strtoul(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtoul_full_empty(void) @@ -2032,7 +2032,7 @@ static void test_qemu_strtoi64_null(void) err = qemu_strtoi64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); g_assert_null(endptr); } @@ -2322,7 +2322,7 @@ static void test_qemu_strtoi64_full_null(void) err = qemu_strtoi64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpint(res, ==, 999); + g_assert_cmpint(res, ==, 0); } static void test_qemu_strtoi64_full_empty(void) @@ -2425,7 +2425,7 @@ static void test_qemu_strtou64_null(void) err = qemu_strtou64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); g_assert_null(endptr); } @@ -2714,7 +2714,7 @@ static void test_qemu_strtou64_full_null(void) err = qemu_strtou64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpuint(res, ==, 999); + g_assert_cmpuint(res, ==, 0); } static void test_qemu_strtou64_full_empty(void) diff --git a/util/cutils.c b/util/cutils.c index c5530a5c2b..edfb71a217 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -384,12 +384,13 @@ static int check_strtox_error(const char *nptr, char *ep, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store INT_MAX in @result, * and return -ERANGE. @@ -410,6 +411,7 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -439,12 +441,13 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store UINT_MAX in @result, * and return -ERANGE. @@ -465,6 +468,7 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -508,12 +512,13 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store LONG_MAX in @result, * and return -ERANGE. @@ -530,6 +535,7 @@ int qemu_strtol(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -550,12 +556,13 @@ int qemu_strtol(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, 0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows @result, store ULONG_MAX in @result, * and return -ERANGE. @@ -573,6 +580,7 @@ int qemu_strtoul(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -601,6 +609,7 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } @@ -628,6 +637,7 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, assert((unsigned) base <= 36 && base != 1); if (!nptr) { + *result = 0; if (endptr) { *endptr = nptr; } From b87ac96651054fa89baab4e3a88a7feee7f92314 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:39 -0500 Subject: [PATCH 19/21] cutils: Use parse_uint in qemu_strtosz for negative rejection Rather than open-coding two different ways to check for an unwanted negative sign, reuse the same code in both functions. That way, if we decide down the road to accept "-0" instead of rejecting it, we have fewer places to change. Also, it means we now get ERANGE instead of EINVAL for negative values in qemu_strtosz, which is reasonable for what it represents. This in turn changes the expected output of a couple of iotests. The change is not quite complete: negative fractional scaled values can trip us up. This will be fixed in a later patch addressing other issues with fractional scaled values. Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-18-eblake@redhat.com> --- tests/qemu-iotests/049.out | 7 ++----- tests/qemu-iotests/178.out.qcow2 | 3 +-- tests/qemu-iotests/178.out.raw | 3 +-- tests/unit/test-cutils.c | 7 +++---- util/cutils.c | 8 ++------ 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 8719c91b48..34e1b452e6 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -92,13 +92,10 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp == 3. Invalid sizes == qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024 -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 -qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 -Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- -and exabytes, respectively. +qemu-img: TEST_DIR/t.qcow2: Value '-1024' is out of range for parameter 'size' qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 index 0d51fe401e..fe193fd5f4 100644 --- a/tests/qemu-iotests/178.out.qcow2 +++ b/tests/qemu-iotests/178.out.qcow2 @@ -13,8 +13,7 @@ qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' qemu-img: --output must be used with human or json as argument. -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img: Unknown file format 'foo' == Size calculation for a new file (human) == diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw index 116241ddef..445e460fad 100644 --- a/tests/qemu-iotests/178.out.raw +++ b/tests/qemu-iotests/178.out.raw @@ -13,8 +13,7 @@ qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' qemu-img: --output must be used with human or json as argument. -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img: Unknown file format 'foo' == Size calculation for a new file (human) == diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index cfe1fc8a17..0e3215a46e 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3519,10 +3519,9 @@ static void test_qemu_strtosz_trailing(void) static void test_qemu_strtosz_erange(void) { /* FIXME negative values fit better as ERANGE */ - do_strtosz(" -0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 3 */); - do_strtosz("-1", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 2 */); - do_strtosz_full("-2M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, - 0 /* FIXME 2 */, -EINVAL, 0); + do_strtosz(" -0", -ERANGE, 0, 3); + do_strtosz("-1", -ERANGE, 0, 2); + do_strtosz_full("-2M", qemu_strtosz, -ERANGE, 0, 2, -EINVAL, 0); do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 4 */); do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 3 */, -EINVAL, 0); diff --git a/util/cutils.c b/util/cutils.c index edfb71a217..e3a49209a9 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -201,6 +201,7 @@ static int64_t suffix_mul(char suffix, int64_t unit) * - hex with scaling suffix, such as 0x20M * - octal, such as 08 * - fractional hex, such as 0x1.8 + * - negative values, including -0 * - floating point exponents, such as 1e3 * * The end pointer will be returned in *end, if not NULL. If there is @@ -226,15 +227,10 @@ static int do_strtosz(const char *nptr, const char **end, int64_t mul; /* Parse integral portion as decimal. */ - retval = qemu_strtou64(nptr, &endptr, 10, &val); + retval = parse_uint(nptr, &endptr, 10, &val); if (retval) { goto out; } - if (memchr(nptr, '-', endptr - nptr) != NULL) { - endptr = nptr; - retval = -EINVAL; - goto out; - } if (val == 0 && (*endptr == 'x' || *endptr == 'X')) { /* Input looks like hex; reparse, and insist on no fraction or suffix. */ retval = qemu_strtou64(nptr, &endptr, 16, &val); From c25b1683443c6d658a82dc1c5587fdb0ae81663c Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:40 -0500 Subject: [PATCH 20/21] cutils: Improve qemu_strtod* error paths Previous patches changed all integral qemu_strto*() error paths to guarantee that *value is never left uninitialized. Do likewise for qemu_strtod. Also, tighten qemu_strtod_finite() to never return a non-finite value (prior to this patch, we were rejecting "inf" with -EINVAL and unspecified result 0.0, but failing "9e999" with -ERANGE and HUGE_VAL - which is infinite on IEEE machines - despite our function claiming to recognize only finite values). Auditing callers, we have no external callers of qemu_strtod, and among the callers of qemu_strtod_finite: - qapi/qobject-input-visitor.c:qobject_input_type_number_keyval() and qapi/string-input-visitor.c:parse_type_number() which reject all errors (does not matter what we store) - utils/cutils.c:do_strtosz() incorrectly assumes that *endptr points to '.' on all failures (that is, it is not distinguishing between EINVAL and ERANGE; and therefore still does the WRONG THING for "9.9e999". The change here does not entirely fix that (a later patch will tackle this more systematically), but at least it fixes the read-out-of-bounds first diagnosed in https://gitlab.com/qemu-project/qemu/-/issues/1629 - our testsuite, which we can update to match what we document Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek CC: qemu-stable@nongnu.org Message-Id: <20230522190441.64278-19-eblake@redhat.com> --- tests/unit/test-cutils.c | 63 +++++++++++++++++++++++----------------- util/cutils.c | 32 +++++++++++--------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 0e3215a46e..1ee410fae4 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -2868,7 +2868,8 @@ static void test_qemu_strtod_einval(void) res = 999; err = qemu_strtod(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_null(endptr); /* not recognizable */ @@ -3101,7 +3102,8 @@ static void test_qemu_strtod_finite_einval(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); /* NULL */ @@ -3110,7 +3112,8 @@ static void test_qemu_strtod_finite_einval(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_null(endptr); /* not recognizable */ @@ -3119,7 +3122,8 @@ static void test_qemu_strtod_finite_einval(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); } @@ -3130,24 +3134,26 @@ static void test_qemu_strtod_finite_erange(void) int err; double res; - /* overflow */ + /* overflow turns into EINVAL */ str = "9e999"; endptr = "somewhere"; res = 999; err = qemu_strtod_finite(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpfloat(res, ==, HUGE_VAL); - g_assert_true(endptr == str + 5); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); str = "-9e+999"; endptr = "somewhere"; res = 999; err = qemu_strtod_finite(str, &endptr, &res); - g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpfloat(res, ==, -HUGE_VAL); - g_assert_true(endptr == str + 7); + g_assert_cmpint(err, ==, -EINVAL); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); + g_assert_true(endptr == str); - /* underflow */ + /* underflow is still possible */ str = "-9e-999"; endptr = "somewhere"; res = 999; @@ -3172,7 +3178,8 @@ static void test_qemu_strtod_finite_nonfinite(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); str = "-infinity"; @@ -3180,7 +3187,8 @@ static void test_qemu_strtod_finite_nonfinite(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); /* not a number */ @@ -3189,7 +3197,8 @@ static void test_qemu_strtod_finite_nonfinite(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); } @@ -3213,7 +3222,8 @@ static void test_qemu_strtod_finite_trailing(void) res = 999; err = qemu_strtod_finite(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 1.0); + g_assert_false(signbit(res)); /* trailing e is not an exponent */ str = ".5e"; @@ -3228,7 +3238,7 @@ static void test_qemu_strtod_finite_trailing(void) res = 999; err = qemu_strtod_finite(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.5); /* trailing ( not part of long NaN */ str = "nan("; @@ -3236,14 +3246,16 @@ static void test_qemu_strtod_finite_trailing(void) res = 999; err = qemu_strtod_finite(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); g_assert_true(endptr == str); endptr = "somewhere"; res = 999; err = qemu_strtod_finite(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); } static void test_qemu_strtod_finite_erange_junk(void) @@ -3269,7 +3281,8 @@ static void test_qemu_strtod_finite_erange_junk(void) res = 999; err = qemu_strtod_finite(str, NULL, &res); g_assert_cmpint(err, ==, -EINVAL); - g_assert_cmpfloat(res, ==, 999.0); + g_assert_cmpfloat(res, ==, 0.0); + g_assert_false(signbit(res)); } typedef int (*qemu_strtosz_fn)(const char *, const char **, uint64_t *); @@ -3507,13 +3520,9 @@ static void test_qemu_strtosz_trailing(void) 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, -EINVAL, 0); - /* - * FIXME overflow in fraction is so buggy it can read beyond bounds - * if we don't stuff extra \0 in our literal - */ - do_strtosz_full("1.5E999\0\0" /* FIXME 1.5E999" */, qemu_strtosz, - 0, 1 /* FIXME EiB * 1.5 */, 8 /* FIXME 4 */, - 0 /* FIXME -EINVAL */, 1 /* FIXME 0 */); + /* FIXME overflow in fraction is still buggy */ + do_strtosz_full("1.5E999", qemu_strtosz, 0, 1 /* FIXME EiB * 1.5 */, + 2 /* FIXME 4 */, -EINVAL, 0); } static void test_qemu_strtosz_erange(void) diff --git a/util/cutils.c b/util/cutils.c index e3a49209a9..bde2da59bd 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -660,12 +660,13 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, * * @nptr may be null, and no conversion is performed then. * - * If no conversion is performed, store @nptr in *@endptr and return - * -EINVAL. + * If no conversion is performed, store @nptr in *@endptr, +0.0 in + * @result, and return -EINVAL. * * If @endptr is null, and the string isn't fully converted, return - * -EINVAL. This is the case when the pointer that would be stored in - * a non-null @endptr points to a character other than '\0'. + * -EINVAL with @result set to the parsed value. This is the case + * when the pointer that would be stored in a non-null @endptr points + * to a character other than '\0'. * * If the conversion overflows, store +/-HUGE_VAL in @result, depending * on the sign, and return -ERANGE. @@ -680,6 +681,7 @@ int qemu_strtod(const char *nptr, const char **endptr, double *result) char *ep; if (!nptr) { + *result = 0.0; if (endptr) { *endptr = nptr; } @@ -694,24 +696,28 @@ int qemu_strtod(const char *nptr, const char **endptr, double *result) /** * Convert string @nptr to a finite double. * - * Works like qemu_strtod(), except that "NaN" and "inf" are rejected - * with -EINVAL and no conversion is performed. + * Works like qemu_strtod(), except that "NaN", "inf", and strings + * that cause ERANGE overflow errors are rejected with -EINVAL as if + * no conversion is performed, storing 0.0 into @result regardless of + * any sign. -ERANGE failures for underflow still preserve the parsed + * sign. */ int qemu_strtod_finite(const char *nptr, const char **endptr, double *result) { - double tmp; + const char *tmp; int ret; - ret = qemu_strtod(nptr, endptr, &tmp); - if (!ret && !isfinite(tmp)) { + ret = qemu_strtod(nptr, &tmp, result); + if (!isfinite(*result)) { if (endptr) { *endptr = nptr; } + *result = 0.0; + ret = -EINVAL; + } else if (endptr) { + *endptr = tmp; + } else if (*tmp) { ret = -EINVAL; - } - - if (ret != -EINVAL) { - *result = tmp; } return ret; } From 42cc08d13ab8e68f76882b216da0b28d06f29e11 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Mon, 22 May 2023 14:04:41 -0500 Subject: [PATCH 21/21] cutils: Improve qemu_strtosz handling of fractions We have several limitations and bugs worth fixing; they are inter-related enough that it is not worth splitting this patch into smaller pieces: * ".5k" should work to specify 512, just as "0.5k" does * "1.9999k" and "1." + "9"*50 + "k" should both produce the same result of 2048 after rounding * "1." + "0"*350 + "1B" should not be treated the same as "1.0B"; underflow in the fraction should not be lost * "7.99e99" and "7.99e999" look similar, but our code was doing a read-out-of-bounds on the latter because it was not expecting ERANGE due to overflow. While we document that scientific notation is not supported, and the previous patch actually fixed qemu_strtod_finite() to no longer return ERANGE overflows, it is easier to pre-filter than to try and determine after the fact if strtod() consumed more than we wanted. Note that this is a low-level semantic change (when endptr is not NULL, we can now successfully parse with a scale of 'E' and then report trailing junk, instead of failing outright with EINVAL); but an earlier commit already argued that this is not a high-level semantic change since the only caller passing in a non-NULL endptr also checks that the tail is whitespace-only. Fixes: https://gitlab.com/qemu-project/qemu/-/issues/1629 Fixes: cf923b78 ("utils: Improve qemu_strtosz() to have 64 bits of precision", 6.0.0) Fixes: 7625a1ed ("utils: Use fixed-point arithmetic in qemu_strtosz", 6.0.0) Signed-off-by: Eric Blake Reviewed-by: Hanna Czenczek Message-Id: <20230522190441.64278-20-eblake@redhat.com> [eblake: tweak function comment for accuracy] --- tests/unit/test-cutils.c | 50 +++++++++------------- util/cutils.c | 92 ++++++++++++++++++++++++++++++---------- 2 files changed, 88 insertions(+), 54 deletions(-) diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 1ee410fae4..227acc5995 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -3408,19 +3408,18 @@ static void test_qemu_strtosz_float(void) /* An empty fraction tail is tolerated */ do_strtosz("1.k", 0, 1024, 3); - /* FIXME An empty fraction head should be tolerated */ - do_strtosz(" .5k", -EINVAL /* FIXME 0 */, 0 /* FIXME 512 */, - 0 /* FIXME 4 */); + /* An empty fraction head is tolerated */ + do_strtosz(" .5k", 0, 512, 4); /* For convenience, we permit values that are not byte-exact */ do_strtosz("12.345M", 0, (uint64_t) (12.345 * MiB + 0.5), 7); - /* FIXME Fraction tail should round correctly */ + /* Fraction tail can round up */ do_strtosz("1.9999k", 0, 2048, 7); do_strtosz("1.9999999999999999999999999999999999999999999999999999k", 0, - 1024 /* FIXME 2048 */, 55); + 2048, 55); - /* FIXME ERANGE underflow in the fraction tail should not matter for 'k' */ + /* ERANGE underflow in the fraction tail does not matter for 'k' */ do_strtosz("1." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3429,7 +3428,7 @@ static void test_qemu_strtosz_float(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1k", 0, 1 /* FIXME 1024 */, 354); + "1k", 0, 1024, 354); } static void test_qemu_strtosz_invalid(void) @@ -3453,10 +3452,9 @@ static void test_qemu_strtosz_invalid(void) do_strtosz("1.1B", -EINVAL, 0, 0); do_strtosz("1.1", -EINVAL, 0, 0); - /* FIXME underflow in the fraction tail should matter for 'B' */ + /* 'B' cannot have any nonzero fraction, even with rounding or underflow */ do_strtosz("1.00001B", -EINVAL, 0, 0); - do_strtosz("1.00000000000000000001B", 0 /* FIXME -EINVAL */, - 1 /* FIXME 0 */, 23 /* FIXME 0 */); + do_strtosz("1.00000000000000000001B", -EINVAL, 0, 0); do_strtosz("1." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3465,8 +3463,7 @@ static void test_qemu_strtosz_invalid(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1B", 0 /* FIXME -EINVAL */, 1 /* FIXME 0 */, - 354 /* FIXME 0 */); + "1B", -EINVAL, 0, 0); /* No hex fractions */ do_strtosz("0x1.8k", -EINVAL, 0, 0); @@ -3512,28 +3509,20 @@ static void test_qemu_strtosz_trailing(void) do_strtosz_full("123-45", qemu_strtosz, 0, 123, 3, -EINVAL, 0); do_strtosz_full(" 123 - 45", qemu_strtosz, 0, 123, 4, -EINVAL, 0); - /* FIXME should stop parse after 'e'. No floating point exponents */ - do_strtosz_full("1.5e1k", qemu_strtosz, -EINVAL /* FIXME 0 */, - 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, - -EINVAL, 0); - do_strtosz_full("1.5E+0k", qemu_strtosz, -EINVAL /* FIXME 0 */, - 0 /* FIXME EiB * 1.5 */, 0 /* FIXME 4 */, - -EINVAL, 0); - - /* FIXME overflow in fraction is still buggy */ - do_strtosz_full("1.5E999", qemu_strtosz, 0, 1 /* FIXME EiB * 1.5 */, - 2 /* FIXME 4 */, -EINVAL, 0); + /* Parse stops at 'e', which is not a floating point exponent */ + do_strtosz_full("1.5e1k", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); + do_strtosz_full("1.5E+0k", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); + do_strtosz_full("1.5E999", qemu_strtosz, 0, EiB * 1.5, 4, -EINVAL, 0); } static void test_qemu_strtosz_erange(void) { - /* FIXME negative values fit better as ERANGE */ + /* no negative values */ do_strtosz(" -0", -ERANGE, 0, 3); do_strtosz("-1", -ERANGE, 0, 2); do_strtosz_full("-2M", qemu_strtosz, -ERANGE, 0, 2, -EINVAL, 0); - do_strtosz(" -.0", -EINVAL /* FIXME -ERANGE */, 0, 0 /* FIXME 4 */); - do_strtosz_full("-.1k", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, - 0 /* FIXME 3 */, -EINVAL, 0); + do_strtosz(" -.0", -ERANGE, 0, 4); + do_strtosz_full("-.1k", qemu_strtosz, -ERANGE, 0, 3, -EINVAL, 0); do_strtosz_full(" -." "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" @@ -3542,17 +3531,16 @@ static void test_qemu_strtosz_erange(void) "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" "00000000000000000000000000000000000000000000000000" - "1M", qemu_strtosz, -EINVAL /* FIXME -ERANGE */, 0, - 0 /* FIXME 354 */, -EINVAL, 0); + "1M", qemu_strtosz, -ERANGE, 0, 354, -EINVAL, 0); /* 2^64; see strtosz_simple for 2^64-1 */ do_strtosz("18446744073709551616", -ERANGE, 0, 20); do_strtosz("20E", -ERANGE, 0, 3); - /* FIXME Fraction tail can cause ERANGE overflow */ + /* Fraction tail can cause ERANGE overflow */ do_strtosz("15.9999999999999999999999999999999999999999999999999999E", - 0 /* FIXME -ERANGE */, 15ULL * EiB /* FIXME 0 */, 56); + -ERANGE, 0, 56); /* EINVAL has priority over ERANGE */ do_strtosz_full("100000Pjunk", qemu_strtosz, -ERANGE, 0, 7, -EINVAL, 0); diff --git a/util/cutils.c b/util/cutils.c index bde2da59bd..25373198ad 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -194,15 +194,19 @@ static int64_t suffix_mul(char suffix, int64_t unit) * - 12345 - decimal, scale determined by @default_suffix and @unit * - 12345{bBkKmMgGtTpPeE} - decimal, scale determined by suffix and @unit * - 12345.678{kKmMgGtTpPeE} - decimal, scale determined by suffix, and - * fractional portion is truncated to byte + * fractional portion is truncated to byte, either side of . may be empty * - 0x7fEE - hexadecimal, unit determined by @default_suffix * * The following are intentionally not supported - * - hex with scaling suffix, such as 0x20M - * - octal, such as 08 - * - fractional hex, such as 0x1.8 - * - negative values, including -0 - * - floating point exponents, such as 1e3 + * - hex with scaling suffix, such as 0x20M or 0x1p3 (both fail with + * -EINVAL), while 0x1b is 27 (not 1 with byte scale) + * - octal, such as 08 (parsed as decimal instead) + * - binary, such as 0b1000 (parsed as 0b with trailing garbage "1000") + * - fractional hex, such as 0x1.8 (parsed as 0 with trailing garbage "x1.8") + * - negative values, including -0 (fail with -ERANGE) + * - floating point exponents, such as 1e3 (parsed as 1e with trailing + * garbage "3") or 0x1p3 (rejected as hex with scaling suffix) + * - non-finite values, such as inf or NaN (fail with -EINVAL) * * The end pointer will be returned in *end, if not NULL. If there is * no fraction, the input can be decimal or hexadecimal; if there is a @@ -221,17 +225,17 @@ static int do_strtosz(const char *nptr, const char **end, uint64_t *result) { int retval; - const char *endptr, *f; + const char *endptr; unsigned char c; - uint64_t val, valf = 0; + uint64_t val = 0, valf = 0; int64_t mul; /* Parse integral portion as decimal. */ retval = parse_uint(nptr, &endptr, 10, &val); - if (retval) { + if (retval == -ERANGE || !nptr) { goto out; } - if (val == 0 && (*endptr == 'x' || *endptr == 'X')) { + if (retval == 0 && val == 0 && (*endptr == 'x' || *endptr == 'X')) { /* Input looks like hex; reparse, and insist on no fraction or suffix. */ retval = qemu_strtou64(nptr, &endptr, 16, &val); if (retval) { @@ -242,26 +246,68 @@ static int do_strtosz(const char *nptr, const char **end, retval = -EINVAL; goto out; } - } else if (*endptr == '.') { + } else if (*endptr == '.' || (endptr == nptr && strchr(nptr, '.'))) { /* * Input looks like a fraction. Make sure even 1.k works - * without fractional digits. If we see an exponent, treat - * the entire input as invalid instead. + * without fractional digits. strtod tries to treat 'e' as an + * exponent, but we want to treat it as a scaling suffix; + * doing this requires modifying a copy of the fraction. */ - double fraction; + double fraction = 0.0; - f = endptr; - retval = qemu_strtod_finite(f, &endptr, &fraction); - if (retval) { + if (retval == 0 && *endptr == '.' && !isdigit(endptr[1])) { + /* If we got here, we parsed at least one digit already. */ endptr++; - } else if (memchr(f, 'e', endptr - f) || memchr(f, 'E', endptr - f)) { - endptr = nptr; - retval = -EINVAL; - goto out; } else { - /* Extract into a 64-bit fixed-point fraction. */ - valf = (uint64_t)(fraction * 0x1p64); + char *e; + const char *tail; + g_autofree char *copy = g_strdup(endptr); + + e = strchr(copy, 'e'); + if (e) { + *e = '\0'; + } + e = strchr(copy, 'E'); + if (e) { + *e = '\0'; + } + /* + * If this is a floating point, we are guaranteed that '.' + * appears before any possible digits in copy. If it is + * not a floating point, strtod will fail. Either way, + * there is now no exponent in copy, so if it parses, we + * know 0.0 <= abs(result) <= 1.0 (after rounding), and + * ERANGE is only possible on underflow which is okay. + */ + retval = qemu_strtod_finite(copy, &tail, &fraction); + endptr += tail - copy; + if (signbit(fraction)) { + retval = -ERANGE; + goto out; + } } + + /* Extract into a 64-bit fixed-point fraction. */ + if (fraction == 1.0) { + if (val == UINT64_MAX) { + retval = -ERANGE; + goto out; + } + val++; + } else if (retval == -ERANGE) { + /* See comments above about underflow */ + valf = 1; + retval = 0; + } else { + /* We want non-zero valf for any non-zero fraction */ + valf = (uint64_t)(fraction * 0x1p64); + if (valf == 0 && fraction > 0.0) { + valf = 1; + } + } + } + if (retval) { + goto out; } c = *endptr; mul = suffix_mul(c, unit);