aboutsummaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/cutils.c263
-rw-r--r--util/guest-random.c4
-rw-r--r--util/qemu-sockets.c10
3 files changed, 174 insertions, 103 deletions
diff --git a/util/cutils.c b/util/cutils.c
index 5887e74..2537319 100644
--- a/util/cutils.c
+++ b/util/cutils.c
@@ -194,46 +194,48 @@ 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
- * - 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
- * 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,
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 = qemu_strtou64(nptr, &endptr, 10, &val);
- if (retval) {
- goto out;
- }
- if (memchr(nptr, '-', endptr - nptr) != NULL) {
- endptr = nptr;
- retval = -EINVAL;
+ retval = parse_uint(nptr, &endptr, 10, &val);
+ 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) {
@@ -244,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) {
@@ -306,11 +350,16 @@ 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) {
*result = val;
+ } else {
+ *result = 0;
+ if (end && retval == -EINVAL) {
+ *end = nptr;
+ }
}
return retval;
@@ -377,12 +426,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.
@@ -391,6 +441,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)
@@ -400,6 +453,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;
}
@@ -429,12 +483,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.
@@ -443,16 +498,19 @@ 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) {
+ *result = 0;
if (endptr) {
*endptr = nptr;
}
@@ -466,14 +524,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);
@@ -488,12 +554,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.
@@ -510,6 +577,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;
}
@@ -530,12 +598,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.
@@ -553,6 +622,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;
}
@@ -581,6 +651,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;
}
@@ -598,6 +669,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)
@@ -606,6 +679,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;
}
@@ -632,12 +706,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.
@@ -652,6 +727,7 @@ int qemu_strtod(const char *nptr, const char **endptr, double *result)
char *ep;
if (!nptr) {
+ *result = 0.0;
if (endptr) {
*endptr = nptr;
}
@@ -666,24 +742,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;
}
@@ -707,32 +787,33 @@ 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
* @base: integer base, between 2 and 36 inclusive, or 0
+ * @value: Destination for parsed integer value
*
* Parse unsigned integer
*
* 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).
+ * 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
+ * bounds, use qemu_strtou64()).
*
* If the integer overflows unsigned long long, set *@value to
* ULLONG_MAX, and return -ERANGE.
*
* 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;
@@ -768,7 +849,12 @@ int parse_uint(const char *s, unsigned long long *value, char **endptr,
out:
*value = val;
- *endptr = endp;
+ if (endptr) {
+ *endptr = endp;
+ } else if (s && *endp) {
+ r = -EINVAL;
+ *value = 0;
+ }
return r;
}
@@ -776,31 +862,16 @@ 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
+ * 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 the 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, unsigned long long *value, int base)
+int parse_uint_full(const char *s, int base, uint64_t *value)
{
- char *endp;
- int r;
-
- r = parse_uint(s, value, &endp, base);
- 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)
diff --git a/util/guest-random.c b/util/guest-random.c
index a24d276..9465dda 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 c06a4dc..892d33f 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;