qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PULL 00/21] NBD and miscellaneous patches for 2023-06-01
@ 2023-06-01 22:02 Eric Blake
  2023-06-01 22:02 ` [PULL 01/21] iotests: Fix test 104 under NBD Eric Blake
                   ` (22 more replies)
  0 siblings, 23 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel

The following changes since commit 19a720b74fde7e859d19f12c66a72e545947a657:

  Merge tag 'tracing-pull-request' of https://gitlab.com/stefanha/qemu into staging (2023-06-01 08:30:29 -0700)

are available in the Git repository at:

  https://repo.or.cz/qemu/ericb.git tags/pull-nbd-2023-06-01

for you to fetch changes up to 58516caac47c4bc7ed3ad6a8e2f565404a563dd3:

  cutils: Improve qemu_strtosz handling of fractions (2023-06-01 16:55:25 -0500)

----------------------------------------------------------------
nbd and misc patches for 2023-06-01

- Eric Blake: Fix iotest 104 for NBD
- Eric Blake: Improve qcow2 spec on padding bytes
- Eric Blake: Fix read-beyond-bounds bug in qemu_strtosz

----------------------------------------------------------------
Eric Blake (21):
      iotests: Fix test 104 under NBD
      qcow2: Explicit mention of padding bytes
      test-cutils: Avoid g_assert in unit tests
      test-cutils: Use g_assert_cmpuint where appropriate
      test-cutils: Test integral qemu_strto* value on failures
      test-cutils: Test more integer corner cases
      cutils: Fix wraparound parsing in qemu_strtoui
      cutils: Document differences between parse_uint and qemu_strtou64
      cutils: Adjust signature of parse_uint[_full]
      cutils: Allow NULL endptr in parse_uint()
      test-cutils: Add coverage of qemu_strtod
      test-cutils: Prepare for upcoming semantic change in qemu_strtosz
      test-cutils: Refactor qemu_strtosz tests for less boilerplate
      cutils: Allow NULL str in qemu_strtosz
      numa: Check for qemu_strtosz_MiB error
      test-cutils: Add more coverage to qemu_strtosz
      cutils: Set value in all qemu_strtosz* error paths
      cutils: Set value in all integral qemu_strto* error paths
      cutils: Use parse_uint in qemu_strtosz for negative rejection
      cutils: Improve qemu_strtod* error paths
      cutils: Improve qemu_strtosz handling of fractions

 docs/interop/qcow2.txt           |    1 +
 include/qemu/cutils.h            |    5 +-
 audio/audio_legacy.c             |    4 +-
 block/gluster.c                  |    4 +-
 block/nfs.c                      |    4 +-
 blockdev.c                       |    4 +-
 contrib/ivshmem-server/main.c    |    4 +-
 hw/core/numa.c                   |   11 +-
 qapi/opts-visitor.c              |   10 +-
 tests/unit/test-cutils.c         | 2469 ++++++++++++++++++++++++++++----------
 ui/vnc.c                         |    4 +-
 util/cutils.c                    |  263 ++--
 util/guest-random.c              |    4 +-
 util/qemu-sockets.c              |   10 +-
 tests/qemu-iotests/common.filter |    4 +-
 tests/qemu-iotests/common.rc     |    3 +-
 tests/qemu-iotests/049.out       |    7 +-
 tests/qemu-iotests/178.out.qcow2 |    3 +-
 tests/qemu-iotests/178.out.raw   |    3 +-
 19 files changed, 2035 insertions(+), 782 deletions(-)

-- 
2.40.1



^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PULL 01/21] iotests: Fix test 104 under NBD
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 02/21] qcow2: Explicit mention of padding bytes Eric Blake
                   ` (21 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel
  Cc: Daniel P . Berrangé, Kevin Wolf, Hanna Reitz,
	open list:Block layer core

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 <nbd://127.0.0.1:0\+> 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 <eblake@redhat.com>
Message-Id: <20230519150216.2599189-1-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
---
 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 6b32c7fbfa1..fc3c64bcb8e 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 f4476b62f7d..d145f08201c 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" \
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 02/21] qcow2: Explicit mention of padding bytes
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
  2023-06-01 22:02 ` [PULL 01/21] iotests: Fix test 104 under NBD Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 03/21] test-cutils: Avoid g_assert in unit tests Eric Blake
                   ` (20 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel
  Cc: Vladimir Sementsov-Ogievskiy, Kevin Wolf, Hanna Reitz,
	open list:qcow2

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 <eblake@redhat.com>
CC: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Message-Id: <20230522184631.47211-1-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
 docs/interop/qcow2.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt
index e7f036c286b..2c4618375ad 100644
--- a/docs/interop/qcow2.txt
+++ b/docs/interop/qcow2.txt
@@ -226,6 +226,7 @@ version 2.
                     <https://www.zlib.net/> in QEMU. However, clusters with the
                     deflate compression type do not have zlib headers.

+        105 - 111:  Padding, contents defined below.

 === Header padding ===

-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 03/21] test-cutils: Avoid g_assert in unit tests
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
  2023-06-01 22:02 ` [PULL 01/21] iotests: Fix test 104 under NBD Eric Blake
  2023-06-01 22:02 ` [PULL 02/21] qcow2: Explicit mention of padding bytes Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 04/21] test-cutils: Use g_assert_cmpuint where appropriate Eric Blake
                   ` (19 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek, Philippe Mathieu-Daudé

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
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 3c4f8754202..0202ac0d5b3 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 <ehabkost@redhat.com>
@@ -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)
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 04/21] test-cutils: Use g_assert_cmpuint where appropriate
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (2 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 03/21] test-cutils: Avoid g_assert in unit tests Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 05/21] test-cutils: Test integral qemu_strto* value on failures Eric Blake
                   ` (18 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 0202ac0d5b3..38bd3990207 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);
 }

-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 05/21] test-cutils: Test integral qemu_strto* value on failures
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (3 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 04/21] test-cutils: Use g_assert_cmpuint where appropriate Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 06/21] test-cutils: Test more integer corner cases Eric Blake
                   ` (17 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 38bd3990207..1eeaf21ae22 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)
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 06/21] test-cutils: Test more integer corner cases
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (4 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 05/21] test-cutils: Test integral qemu_strto* value on failures Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-02 12:34   ` Eric Blake
  2023-06-01 22:02 ` [PULL 07/21] cutils: Fix wraparound parsing in qemu_strtoui Eric Blake
                   ` (16 subsequent siblings)
  22 siblings, 1 reply; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <20230522190441.64278-5-eblake@redhat.com>
[eblake: fix a few typos spotted by Hanna]
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
---
 tests/unit/test-cutils.c | 929 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 864 insertions(+), 65 deletions(-)

diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c
index 1eeaf21ae22..011123a2111 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_max(void)
+{
+    unsigned long long i = 999;
+    char f = 'X';
+    char *endptr = &f;
+    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 = 999;
+    unsigned long long i;
     char f = 'X';
-    char *endptr = &f;
-    const char *str = "99999999999999999999999999999999999999";
+    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,7 +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);
+    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));
+
+    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_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));
+}
+
+static void test_qemu_strtoi_min(void)
+{
+    char *str = g_strdup_printf("%d", INT_MIN);
     char f = 'X';
     const char *endptr = &f;
     int res = 999;
@@ -418,31 +537,95 @@ static void test_qemu_strtoi_overflow(void)

     err = qemu_strtoi(str, &endptr, 0, &res);

-    g_assert_cmpint(err, ==, -ERANGE);
-    g_assert_cmpint(res, ==, INT_MAX);
+    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)
 {
-    char *str = g_strdup_printf("%lld", (long long)INT_MIN - 1ll);
-    char f = 'X';
-    const char *endptr = &f;
-    int res = 999;
+    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));
-    g_free(str);
 }

 static void test_qemu_strtoi_negative(void)
 {
-    const char *str = "  \t -321";
+    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;
@@ -451,7 +634,7 @@ static void test_qemu_strtoi_negative(void)
     err = qemu_strtoi(str, &endptr, 0, &res);

     g_assert_cmpint(err, ==, 0);
-    g_assert_cmpint(res, ==, -321);
+    g_assert_cmpint(res, ==, 0);
     g_assert_true(endptr == str + strlen(str));
 }

@@ -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_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_cmphex(res, ==, UINT_MAX);
+    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, ==, 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, ==, (unsigned int)-1);
+    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,7 +1365,50 @@ static void test_qemu_strtol_max(void)

 static void test_qemu_strtol_overflow(void)
 {
-    const char *str = "99999999999999999999999999999999999999999999";
+    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_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_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_min(void)
+{
+    char *str = g_strdup_printf("%ld", LONG_MIN);
     char f = 'X';
     const char *endptr = &f;
     long res = 999;
@@ -1028,21 +1416,50 @@ static void test_qemu_strtol_overflow(void)

     err = qemu_strtol(str, &endptr, 0, &res);

-    g_assert_cmpint(err, ==, -ERANGE);
-    g_assert_cmpint(res, ==, LONG_MAX);
+    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 = "-99999999999999999999999999999999999999999999";
-    char f = 'X';
-    const char *endptr = &f;
-    long res = 999;
+    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_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_cmphex(res, ==, ULONG_MAX);
+    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, ==, 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, ==, -1ul);
+    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,22 +2195,39 @@ static void test_qemu_strtoi64_max(void)

 static void test_qemu_strtoi64_overflow(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 = "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, ==, LLONG_MAX);
+    g_assert_cmpint(res, ==, INT64_MAX);
     g_assert_true(endptr == str + strlen(str));
 }

-static void test_qemu_strtoi64_underflow(void)
+static void test_qemu_strtoi64_min(void)
 {
-    const char *str = "-99999999999999999999999999999999999999999999";
+    char *str = g_strdup_printf("%lld", LLONG_MIN);
     char f = 'X';
     const char *endptr = &f;
     int64_t res = 999;
@@ -1649,9 +2235,42 @@ static void test_qemu_strtoi64_underflow(void)

     err = qemu_strtoi64(str, &endptr, 0, &res);

-    g_assert_cmpint(err, ==, -ERANGE);
+    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;
+    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, ==, 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));
 }

 static void test_qemu_strtoi64_negative(void)
@@ -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_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_cmphex(res, ==, ULLONG_MAX);
+    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_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_cmphex(res, ==, -1ull);
+    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);
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 07/21] cutils: Fix wraparound parsing in qemu_strtoui
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (5 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 06/21] test-cutils: Test more integer corner cases Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-03  8:17   ` Michael Tokarev
  2023-06-01 22:02 ` [PULL 08/21] cutils: Document differences between parse_uint and qemu_strtou64 Eric Blake
                   ` (15 subsequent siblings)
  22 siblings, 1 reply; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: qemu-stable, Hanna Czenczek

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 <eblake@redhat.com>
CC: qemu-stable@nongnu.org
Message-Id: <20230522190441.64278-6-eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
---
 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 011123a2111..ce71900cb73 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 5887e744140..9b6ce9179c4 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);
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 08/21] cutils: Document differences between parse_uint and qemu_strtou64
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (6 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 07/21] cutils: Fix wraparound parsing in qemu_strtoui Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 09/21] cutils: Adjust signature of parse_uint[_full] Eric Blake
                   ` (14 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 9b6ce9179c4..36c14b769fd 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)
 {
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 09/21] cutils: Adjust signature of parse_uint[_full]
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (7 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 08/21] cutils: Document differences between parse_uint and qemu_strtou64 Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-02  6:16   ` Markus Armbruster
  2023-06-01 22:02 ` [PULL 10/21] cutils: Allow NULL endptr in parse_uint() Eric Blake
                   ` (13 subsequent siblings)
  22 siblings, 1 reply; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel
  Cc: Hanna Czenczek, Gerd Hoffmann, Marc-André Lureau, Kevin Wolf,
	Peter Lieven, Markus Armbruster, Michael Roth,
	Daniel P. Berrangé, open list:GLUSTER, open list:GLUSTER

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.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Message-Id: <20230522190441.64278-8-eblake@redhat.com>
---
 include/qemu/cutils.h         |   5 +-
 audio/audio_legacy.c          |   4 +-
 block/gluster.c               |   4 +-
 block/nfs.c                   |   4 +-
 blockdev.c                    |   4 +-
 contrib/ivshmem-server/main.c |   4 +-
 qapi/opts-visitor.c           |  10 +--
 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, 85 insertions(+), 100 deletions(-)

diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h
index 92c436d8c70..92c927a6a35 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/audio/audio_legacy.c b/audio/audio_legacy.c
index b848001ff70..dc72ba55e9a 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 185a83e5e53..ad5fadbe793 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 8f89ece69fa..c24df49747d 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 db2725fe741..e6eba61484a 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 224dbeb547e..5901f17707e 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/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 587f31baf6b..8812d23677a 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,17 +471,17 @@ 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 && val <= UINT64_MAX) {
         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 &&
+            if (parse_uint_full(str, 0, &val2) == 0 &&
                 val2 <= UINT64_MAX && val <= val2 &&
                 val2 - val < OPTS_VISITOR_RANGE_MAX) {
                 ov->range_next.u = val;
diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c
index ce71900cb73..65041bd3974 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 9d8a24dd8a6..92964dcc0c0 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 36c14b769fd..0e279a531aa 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 a24d27624c6..9465dda085d 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 c06a4dce77c..892d33f5e6b 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;
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 10/21] cutils: Allow NULL endptr in parse_uint()
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (8 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 09/21] cutils: Adjust signature of parse_uint[_full] Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 11/21] test-cutils: Add coverage of qemu_strtod Eric Blake
                   ` (12 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 65041bd3974..20ab0ecb673 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 0e279a531aa..56a2aced8d4 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)
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 11/21] test-cutils: Add coverage of qemu_strtod
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (9 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 10/21] cutils: Allow NULL endptr in parse_uint() Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 12/21] test-cutils: Prepare for upcoming semantic change in qemu_strtosz Eric Blake
                   ` (11 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 20ab0ecb673..e05572cd92c 100644
--- a/tests/unit/test-cutils.c
+++ b/tests/unit/test-cutils.c
@@ -25,6 +25,8 @@
  * THE SOFTWARE.
  */

+#include <math.h>
+
 #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",
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 12/21] test-cutils: Prepare for upcoming semantic change in qemu_strtosz
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (10 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 11/21] test-cutils: Add coverage of qemu_strtod Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 13/21] test-cutils: Refactor qemu_strtosz tests for less boilerplate Eric Blake
                   ` (10 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 e05572cd92c..3a095272d0f 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)
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 13/21] test-cutils: Refactor qemu_strtosz tests for less boilerplate
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (11 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 12/21] test-cutils: Prepare for upcoming semantic change in qemu_strtosz Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 14/21] cutils: Allow NULL str in qemu_strtosz Eric Blake
                   ` (9 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 3a095272d0f..96bc9d1f202 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)
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 14/21] cutils: Allow NULL str in qemu_strtosz
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (12 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 13/21] test-cutils: Refactor qemu_strtosz tests for less boilerplate Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:02 ` [PULL 15/21] numa: Check for qemu_strtosz_MiB error Eric Blake
                   ` (8 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel; +Cc: Philippe Mathieu-Daudé, Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 96bc9d1f202..26e3ba4b9f3 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 56a2aced8d4..1dc67d201dc 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) {
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 15/21] numa: Check for qemu_strtosz_MiB error
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (13 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 14/21] cutils: Allow NULL str in qemu_strtosz Eric Blake
@ 2023-06-01 22:02 ` Eric Blake
  2023-06-01 22:03 ` [PULL 16/21] test-cutils: Add more coverage to qemu_strtosz Eric Blake
                   ` (7 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:02 UTC (permalink / raw)
  To: qemu-devel
  Cc: Hanna Czenczek, Eduardo Habkost, Marcel Apfelbaum,
	Philippe Mathieu-Daudé, Yanan Wang

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 d8d36b16d80..f08956ddb0f 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) {
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 16/21] test-cutils: Add more coverage to qemu_strtosz
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (14 preceding siblings ...)
  2023-06-01 22:02 ` [PULL 15/21] numa: Check for qemu_strtosz_MiB error Eric Blake
@ 2023-06-01 22:03 ` Eric Blake
  2023-06-01 22:03 ` [PULL 17/21] cutils: Set value in all qemu_strtosz* error paths Eric Blake
                   ` (6 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 26e3ba4b9f3..a629ef2ea39 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)
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 17/21] cutils: Set value in all qemu_strtosz* error paths
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (15 preceding siblings ...)
  2023-06-01 22:03 ` [PULL 16/21] test-cutils: Add more coverage to qemu_strtosz Eric Blake
@ 2023-06-01 22:03 ` Eric Blake
  2023-06-01 22:03 ` [PULL 18/21] cutils: Set value in all integral qemu_strto* " Eric Blake
                   ` (5 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 a629ef2ea39..2189ebc92f3 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 1dc67d201dc..c5530a5c2be 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;
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 18/21] cutils: Set value in all integral qemu_strto* error paths
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (16 preceding siblings ...)
  2023-06-01 22:03 ` [PULL 17/21] cutils: Set value in all qemu_strtosz* error paths Eric Blake
@ 2023-06-01 22:03 ` Eric Blake
  2023-06-01 22:03 ` [PULL 19/21] cutils: Use parse_uint in qemu_strtosz for negative rejection Eric Blake
                   ` (4 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 2189ebc92f3..e5b780672d1 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 c5530a5c2be..edfb71a2171 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;
         }
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 19/21] cutils: Use parse_uint in qemu_strtosz for negative rejection
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (17 preceding siblings ...)
  2023-06-01 22:03 ` [PULL 18/21] cutils: Set value in all integral qemu_strto* " Eric Blake
@ 2023-06-01 22:03 ` Eric Blake
  2023-06-01 22:03 ` [PULL 20/21] cutils: Improve qemu_strtod* error paths Eric Blake
                   ` (3 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek, Kevin Wolf, open list:Block layer core

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Message-Id: <20230522190441.64278-18-eblake@redhat.com>
---
 tests/unit/test-cutils.c         | 7 +++----
 util/cutils.c                    | 8 ++------
 tests/qemu-iotests/049.out       | 7 ++-----
 tests/qemu-iotests/178.out.qcow2 | 3 +--
 tests/qemu-iotests/178.out.raw   | 3 +--
 5 files changed, 9 insertions(+), 19 deletions(-)

diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c
index e5b780672d1..c2dbed9eda9 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 edfb71a2171..e3a49209a94 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);
diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out
index 8719c91b483..34e1b452e6e 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 0d51fe401ec..fe193fd5f4f 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 116241ddef2..445e460fad9 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) ==
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 20/21] cutils: Improve qemu_strtod* error paths
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (18 preceding siblings ...)
  2023-06-01 22:03 ` [PULL 19/21] cutils: Use parse_uint in qemu_strtosz for negative rejection Eric Blake
@ 2023-06-01 22:03 ` Eric Blake
  2023-06-01 22:03 ` [PULL 21/21] cutils: Improve qemu_strtosz handling of fractions Eric Blake
                   ` (2 subsequent siblings)
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek, qemu-stable

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
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 c2dbed9eda9..0a589567461 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 e3a49209a94..bde2da59bdd 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;
 }
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PULL 21/21] cutils: Improve qemu_strtosz handling of fractions
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (19 preceding siblings ...)
  2023-06-01 22:03 ` [PULL 20/21] cutils: Improve qemu_strtod* error paths Eric Blake
@ 2023-06-01 22:03 ` Eric Blake
  2023-06-02  3:58 ` [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Richard Henderson
  2023-06-02  6:32 ` Conclusion of yet another expensive UI folly (was: [PULL 00/21] NBD and miscellaneous patches for 2023-06-01) Markus Armbruster
  22 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-01 22:03 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

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 <eblake@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Message-Id: <20230522190441.64278-20-eblake@redhat.com>
[eblake: tweak function comment for accuracy]
---
 tests/unit/test-cutils.c | 50 +++++++++-------------
 util/cutils.c            | 90 ++++++++++++++++++++++++++++++----------
 2 files changed, 87 insertions(+), 53 deletions(-)

diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c
index 0a589567461..1db411489f0 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 bde2da59bdd..25373198adc 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,27 +246,69 @@ 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. */
+            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);
     if (mul > 0) {
-- 
2.40.1



^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PULL 00/21] NBD and miscellaneous patches for 2023-06-01
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (20 preceding siblings ...)
  2023-06-01 22:03 ` [PULL 21/21] cutils: Improve qemu_strtosz handling of fractions Eric Blake
@ 2023-06-02  3:58 ` Richard Henderson
  2023-06-02 12:27   ` Eric Blake
  2023-06-02  6:32 ` Conclusion of yet another expensive UI folly (was: [PULL 00/21] NBD and miscellaneous patches for 2023-06-01) Markus Armbruster
  22 siblings, 1 reply; 32+ messages in thread
From: Richard Henderson @ 2023-06-02  3:58 UTC (permalink / raw)
  To: Eric Blake, qemu-devel

On 6/1/23 15:02, Eric Blake wrote:
> Eric Blake (21):
>        iotests: Fix test 104 under NBD
>        qcow2: Explicit mention of padding bytes
>        test-cutils: Avoid g_assert in unit tests
>        test-cutils: Use g_assert_cmpuint where appropriate
>        test-cutils: Test integral qemu_strto* value on failures
>        test-cutils: Test more integer corner cases
>        cutils: Fix wraparound parsing in qemu_strtoui
>        cutils: Document differences between parse_uint and qemu_strtou64
>        cutils: Adjust signature of parse_uint[_full]
>        cutils: Allow NULL endptr in parse_uint()
>        test-cutils: Add coverage of qemu_strtod
>        test-cutils: Prepare for upcoming semantic change in qemu_strtosz
>        test-cutils: Refactor qemu_strtosz tests for less boilerplate
>        cutils: Allow NULL str in qemu_strtosz
>        numa: Check for qemu_strtosz_MiB error
>        test-cutils: Add more coverage to qemu_strtosz
>        cutils: Set value in all qemu_strtosz* error paths
>        cutils: Set value in all integral qemu_strto* error paths
>        cutils: Use parse_uint in qemu_strtosz for negative rejection
>        cutils: Improve qemu_strtod* error paths
>        cutils: Improve qemu_strtosz handling of fractions

This is failing on Windows (32 and 64-bit):

https://gitlab.com/qemu-project/qemu/-/jobs/4399466166#L3524
https://gitlab.com/qemu-project/qemu/-/jobs/4399466165#L3332

|  21/135 /cutils/qemu_strtol/overflow - 
ERROR:../tests/unit/test-cutils.c:1387:test_qemu_strtol_overflow: assertion failed (res == 
LONG_MIN): (2147483647 == -2147483648) FAIL

It seems to have returned LONG_MAX instead of LONG_MIN.


r~


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PULL 09/21] cutils: Adjust signature of parse_uint[_full]
  2023-06-01 22:02 ` [PULL 09/21] cutils: Adjust signature of parse_uint[_full] Eric Blake
@ 2023-06-02  6:16   ` Markus Armbruster
  2023-06-02 12:22     ` Eric Blake
  0 siblings, 1 reply; 32+ messages in thread
From: Markus Armbruster @ 2023-06-02  6:16 UTC (permalink / raw)
  To: Eric Blake
  Cc: qemu-devel, Hanna Czenczek, Gerd Hoffmann, Marc-André Lureau,
	Kevin Wolf, Peter Lieven, Michael Roth, Daniel P. Berrangé,
	open list:GLUSTER, open list:GLUSTER

Sorry for being late to the party...

Eric Blake <eblake@redhat.com> writes:

> 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.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
> Message-Id: <20230522190441.64278-8-eblake@redhat.com>

[...]

> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index 587f31baf6b..8812d23677a 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;

val changes from unsigned long long, which is at least 64 bits, to
uint64_t, which is exactly 64 bits.

> +    const char *endptr;
>
>      if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
>          *obj = ov->range_next.u;
> @@ -471,17 +471,17 @@ 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 && val <= UINT64_MAX) {

val <= UINT64_MAX is now useless, isn't it?

>          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;

val2 changes from unsigned long long, which is at least 64 bits, to
uint64_t, which is exactly 64 bits.

>              str = endptr + 1;
> -            if (parse_uint_full(str, &val2, 0) == 0 &&
> +            if (parse_uint_full(str, 0, &val2) == 0 &&
>                  val2 <= UINT64_MAX && val <= val2 &&

val2 <= UINT64_MAX is now useless, isn't it?

>                  val2 - val < OPTS_VISITOR_RANGE_MAX) {
>                  ov->range_next.u = val;

[...]



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Conclusion of yet another expensive UI folly (was: [PULL 00/21] NBD and miscellaneous patches for 2023-06-01)
  2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
                   ` (21 preceding siblings ...)
  2023-06-02  3:58 ` [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Richard Henderson
@ 2023-06-02  6:32 ` Markus Armbruster
  2023-06-02 12:29   ` Eric Blake
  22 siblings, 1 reply; 32+ messages in thread
From: Markus Armbruster @ 2023-06-02  6:32 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Pattern:

First, one of us gets a bright idea on user-friendly interface (here:
fractional sizes like 1.5M).  Objections, if any, get brushed aside.

Then the thing sprouts warts, tentacles, sores, and starts to give off
that sickly-sweet smell of bugs feasting on misguided ideas.

Until one of us spends a lot more time on containing and reducing the
damage than the thing could ever be worth.

Cobbler, stick to your last.

Thanks, Eric!



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PULL 09/21] cutils: Adjust signature of parse_uint[_full]
  2023-06-02  6:16   ` Markus Armbruster
@ 2023-06-02 12:22     ` Eric Blake
  0 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-02 12:22 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Hanna Czenczek, Gerd Hoffmann, Marc-André Lureau,
	Kevin Wolf, Peter Lieven, Michael Roth, Daniel P. Berrangé,
	open list:GLUSTER, open list:GLUSTER

On Fri, Jun 02, 2023 at 08:16:38AM +0200, Markus Armbruster wrote:
> Sorry for being late to the party...
> 
> Eric Blake <eblake@redhat.com> writes:
> 
> > 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.
> >
> > Signed-off-by: Eric Blake <eblake@redhat.com>
> > Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
> > Message-Id: <20230522190441.64278-8-eblake@redhat.com>
> 
> [...]
> 
> > diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> > index 587f31baf6b..8812d23677a 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;
> 
> val changes from unsigned long long, which is at least 64 bits, to
> uint64_t, which is exactly 64 bits.

Except that we have:

util/cutils.c:    QEMU_BUILD_BUG_ON(sizeof(uint64_t) != sizeof(unsigned long long));

proving that all of our target platforms have unsigned long long at
the same size (but not necessarily same rank) as uint64_t...

> 
> > +    const char *endptr;
> >
> >      if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
> >          *obj = ov->range_next.u;
> > @@ -471,17 +471,17 @@ 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 && val <= UINT64_MAX) {
> 
> val <= UINT64_MAX is now useless, isn't it?

...so we would have failed to build if the condition could have ever
been true before this patch.  The dead condition is thus pre-existing,
but I will touch it up, since I have to respin to work around a mingw
bug anyways.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PULL 00/21] NBD and miscellaneous patches for 2023-06-01
  2023-06-02  3:58 ` [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Richard Henderson
@ 2023-06-02 12:27   ` Eric Blake
  0 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-02 12:27 UTC (permalink / raw)
  To: Richard Henderson; +Cc: qemu-devel

On Thu, Jun 01, 2023 at 08:58:46PM -0700, Richard Henderson wrote:
> On 6/1/23 15:02, Eric Blake wrote:
> > Eric Blake (21):
> >        iotests: Fix test 104 under NBD
> >        qcow2: Explicit mention of padding bytes
> >        test-cutils: Avoid g_assert in unit tests
> >        test-cutils: Use g_assert_cmpuint where appropriate
> >        test-cutils: Test integral qemu_strto* value on failures
> >        test-cutils: Test more integer corner cases
> >        cutils: Fix wraparound parsing in qemu_strtoui
> >        cutils: Document differences between parse_uint and qemu_strtou64
> >        cutils: Adjust signature of parse_uint[_full]
> >        cutils: Allow NULL endptr in parse_uint()
> >        test-cutils: Add coverage of qemu_strtod
> >        test-cutils: Prepare for upcoming semantic change in qemu_strtosz
> >        test-cutils: Refactor qemu_strtosz tests for less boilerplate
> >        cutils: Allow NULL str in qemu_strtosz
> >        numa: Check for qemu_strtosz_MiB error
> >        test-cutils: Add more coverage to qemu_strtosz
> >        cutils: Set value in all qemu_strtosz* error paths
> >        cutils: Set value in all integral qemu_strto* error paths
> >        cutils: Use parse_uint in qemu_strtosz for negative rejection
> >        cutils: Improve qemu_strtod* error paths
> >        cutils: Improve qemu_strtosz handling of fractions
> 
> This is failing on Windows (32 and 64-bit):
> 
> https://gitlab.com/qemu-project/qemu/-/jobs/4399466166#L3524
> https://gitlab.com/qemu-project/qemu/-/jobs/4399466165#L3332
> 
> |  21/135 /cutils/qemu_strtol/overflow -
> ERROR:../tests/unit/test-cutils.c:1387:test_qemu_strtol_overflow: assertion
> failed (res == LONG_MIN): (2147483647 == -2147483648) FAIL
> 
> It seems to have returned LONG_MAX instead of LONG_MIN.

Gah.  I see the problem; it is a copy/paste typo in part of
test-cutils.c guarded by 'if (LONG_MAX == INT_MAX)' and therefore
doesn't fire on platforms with 64-bit long.  Will respin the pull
request to fix it.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Conclusion of yet another expensive UI folly (was: [PULL 00/21] NBD and miscellaneous patches for 2023-06-01)
  2023-06-02  6:32 ` Conclusion of yet another expensive UI folly (was: [PULL 00/21] NBD and miscellaneous patches for 2023-06-01) Markus Armbruster
@ 2023-06-02 12:29   ` Eric Blake
  2023-06-02 13:02     ` Conclusion of yet another expensive UI folly Markus Armbruster
  0 siblings, 1 reply; 32+ messages in thread
From: Eric Blake @ 2023-06-02 12:29 UTC (permalink / raw)
  To: Markus Armbruster; +Cc: qemu-devel

On Fri, Jun 02, 2023 at 08:32:34AM +0200, Markus Armbruster wrote:
> Pattern:
> 
> First, one of us gets a bright idea on user-friendly interface (here:
> fractional sizes like 1.5M).  Objections, if any, get brushed aside.
> 
> Then the thing sprouts warts, tentacles, sores, and starts to give off
> that sickly-sweet smell of bugs feasting on misguided ideas.
> 
> Until one of us spends a lot more time on containing and reducing the
> damage than the thing could ever be worth.
> 
> Cobbler, stick to your last.
> 
> Thanks, Eric!

The scary part was that I found several other bugs completely
unrelated to the read-out-of-bounds, merely by increasing unit test
coverage.  When Hanna first pointed me to the problem asking if a
5-line patch would work, I was not expecting it to blow up into a
series touching more than 1000 lines (true, most of those lines were
in the unit tests).

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PULL 06/21] test-cutils: Test more integer corner cases
  2023-06-01 22:02 ` [PULL 06/21] test-cutils: Test more integer corner cases Eric Blake
@ 2023-06-02 12:34   ` Eric Blake
  0 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-02 12:34 UTC (permalink / raw)
  To: qemu-devel; +Cc: Hanna Czenczek

On Thu, Jun 01, 2023 at 05:02:50PM -0500, Eric Blake wrote:
> 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.
> 
>  static void test_qemu_strtol_overflow(void)
>  {
> -    const char *str = "99999999999999999999999999999999999999999999";
> +    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_MIN);
> +        g_assert_true(endptr == str + strlen(str));
> +    }

Copy-paste failure, but only visible on platforms like mingw where
long is 32-bits; I had copied this...

> 
>  static void test_qemu_strtol_underflow(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 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));
> +    }

...from here, but failed to s/MIN/MAX/ when dealing with the changed
sign.  Yay for CI catching it.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: Conclusion of yet another expensive UI folly
  2023-06-02 12:29   ` Eric Blake
@ 2023-06-02 13:02     ` Markus Armbruster
  0 siblings, 0 replies; 32+ messages in thread
From: Markus Armbruster @ 2023-06-02 13:02 UTC (permalink / raw)
  To: Eric Blake; +Cc: qemu-devel

Eric Blake <eblake@redhat.com> writes:

> The scary part was that I found several other bugs completely
> unrelated to the read-out-of-bounds, merely by increasing unit test
> coverage.  When Hanna first pointed me to the problem asking if a
> 5-line patch would work, I was not expecting it to blow up into a
> series touching more than 1000 lines (true, most of those lines were
> in the unit tests).

You spot vermin hiding under a rock.  What will you likely see when you
lift the rock?

Thanks again for lifting the rock :)



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PULL 07/21] cutils: Fix wraparound parsing in qemu_strtoui
  2023-06-01 22:02 ` [PULL 07/21] cutils: Fix wraparound parsing in qemu_strtoui Eric Blake
@ 2023-06-03  8:17   ` Michael Tokarev
  2023-06-05 13:32     ` Eric Blake
  0 siblings, 1 reply; 32+ messages in thread
From: Michael Tokarev @ 2023-06-03  8:17 UTC (permalink / raw)
  To: Eric Blake, qemu-devel; +Cc: qemu-stable, Hanna Czenczek

02.06.2023 01:02, Eric Blake пишет:
> 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 <eblake@redhat.com>
> CC: qemu-stable@nongnu.org

Trying to pick this one up for -stable. The implementation changes are good.
But the testsuite changes are.. difficult.  The thing is that testsuite changes
(here and in the other -stable patch) applies on top of previous changes in
there (in the same series), which, in turn, requires other previous code changes
in the implementation to succeed.

For example, this patch changes test_qemu_strtoui_overflow() which was introduced
in previous patch "Test more integer corner cases" from this series and further
modified in "Test integral qemu_strto* value on failures" one.  Picking them result
in testsuite failing due to missing previous code changes.

I tried to drop just the testsuite changes, but the result is that the testsuite
fails with fixed code :)

It's quite fun situation actually, like it fails no matter what you do, one way
or another.

I'll try to find the most stright-forward way from here.  Good stuff.

Thanks,

/mjt



^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PULL 07/21] cutils: Fix wraparound parsing in qemu_strtoui
  2023-06-03  8:17   ` Michael Tokarev
@ 2023-06-05 13:32     ` Eric Blake
  0 siblings, 0 replies; 32+ messages in thread
From: Eric Blake @ 2023-06-05 13:32 UTC (permalink / raw)
  To: Michael Tokarev; +Cc: qemu-devel, qemu-stable, Hanna Czenczek

On Sat, Jun 03, 2023 at 11:17:27AM +0300, Michael Tokarev wrote:
> 02.06.2023 01:02, Eric Blake пишет:
> > 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 <eblake@redhat.com>
> > CC: qemu-stable@nongnu.org
> 
> Trying to pick this one up for -stable. The implementation changes are good.
> But the testsuite changes are.. difficult.  The thing is that testsuite changes
> (here and in the other -stable patch) applies on top of previous changes in
> there (in the same series), which, in turn, requires other previous code changes
> in the implementation to succeed.

Yeah, I did wonder if it is worth even trying to get this for stable.
The series is inter-tangled enough that it feels like an
all-or-nothing approach may be easiest - but all means 19 patches and
hundreds of lines of testsuite additions.  My other argument when
first posting this to qemu-stable was to just declare that these have
been broken long enough that it is not a recent regression, and users
are unlikely to be supplying command-line or QMP strings to tickle
these bugs, so not backporting may be okay.

> 
> For example, this patch changes test_qemu_strtoui_overflow() which was introduced
> in previous patch "Test more integer corner cases" from this series and further
> modified in "Test integral qemu_strto* value on failures" one.  Picking them result
> in testsuite failing due to missing previous code changes.
> 
> I tried to drop just the testsuite changes, but the result is that the testsuite
> fails with fixed code :)
> 
> It's quite fun situation actually, like it fails no matter what you do, one way
> or another.
> 
> I'll try to find the most stright-forward way from here.  Good stuff.

Let me know how I can help in deciding what, if any, is worth backporting.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org



^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2023-06-05 13:34 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-06-01 22:02 [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Eric Blake
2023-06-01 22:02 ` [PULL 01/21] iotests: Fix test 104 under NBD Eric Blake
2023-06-01 22:02 ` [PULL 02/21] qcow2: Explicit mention of padding bytes Eric Blake
2023-06-01 22:02 ` [PULL 03/21] test-cutils: Avoid g_assert in unit tests Eric Blake
2023-06-01 22:02 ` [PULL 04/21] test-cutils: Use g_assert_cmpuint where appropriate Eric Blake
2023-06-01 22:02 ` [PULL 05/21] test-cutils: Test integral qemu_strto* value on failures Eric Blake
2023-06-01 22:02 ` [PULL 06/21] test-cutils: Test more integer corner cases Eric Blake
2023-06-02 12:34   ` Eric Blake
2023-06-01 22:02 ` [PULL 07/21] cutils: Fix wraparound parsing in qemu_strtoui Eric Blake
2023-06-03  8:17   ` Michael Tokarev
2023-06-05 13:32     ` Eric Blake
2023-06-01 22:02 ` [PULL 08/21] cutils: Document differences between parse_uint and qemu_strtou64 Eric Blake
2023-06-01 22:02 ` [PULL 09/21] cutils: Adjust signature of parse_uint[_full] Eric Blake
2023-06-02  6:16   ` Markus Armbruster
2023-06-02 12:22     ` Eric Blake
2023-06-01 22:02 ` [PULL 10/21] cutils: Allow NULL endptr in parse_uint() Eric Blake
2023-06-01 22:02 ` [PULL 11/21] test-cutils: Add coverage of qemu_strtod Eric Blake
2023-06-01 22:02 ` [PULL 12/21] test-cutils: Prepare for upcoming semantic change in qemu_strtosz Eric Blake
2023-06-01 22:02 ` [PULL 13/21] test-cutils: Refactor qemu_strtosz tests for less boilerplate Eric Blake
2023-06-01 22:02 ` [PULL 14/21] cutils: Allow NULL str in qemu_strtosz Eric Blake
2023-06-01 22:02 ` [PULL 15/21] numa: Check for qemu_strtosz_MiB error Eric Blake
2023-06-01 22:03 ` [PULL 16/21] test-cutils: Add more coverage to qemu_strtosz Eric Blake
2023-06-01 22:03 ` [PULL 17/21] cutils: Set value in all qemu_strtosz* error paths Eric Blake
2023-06-01 22:03 ` [PULL 18/21] cutils: Set value in all integral qemu_strto* " Eric Blake
2023-06-01 22:03 ` [PULL 19/21] cutils: Use parse_uint in qemu_strtosz for negative rejection Eric Blake
2023-06-01 22:03 ` [PULL 20/21] cutils: Improve qemu_strtod* error paths Eric Blake
2023-06-01 22:03 ` [PULL 21/21] cutils: Improve qemu_strtosz handling of fractions Eric Blake
2023-06-02  3:58 ` [PULL 00/21] NBD and miscellaneous patches for 2023-06-01 Richard Henderson
2023-06-02 12:27   ` Eric Blake
2023-06-02  6:32 ` Conclusion of yet another expensive UI folly (was: [PULL 00/21] NBD and miscellaneous patches for 2023-06-01) Markus Armbruster
2023-06-02 12:29   ` Eric Blake
2023-06-02 13:02     ` Conclusion of yet another expensive UI folly Markus Armbruster

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).