From 7b045c8ce95da2ff2e2c3e8c3f023232b0374631 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Wed, 30 Apr 2014 20:11:09 -0600 Subject: [PATCH] util: new stricter unsigned int parsing strtoul() is required to parse negative numbers as their twos-complement positive counterpart. But sometimes we want to reject negative numbers. Add new functions to do this. The 'p' suffix is a mnemonic for 'positive' (technically it also parses 0, but 'non-negative' doesn't lend itself to a nice one-letter suffix). * src/util/virstring.h (virStrToLong_uip, virStrToLong_ulp) (virStrToLong_ullp): New prototypes. * src/util/virstring.c (virStrToLong_uip, virStrToLong_ulp) (virStrToLong_ullp): New functions. * src/libvirt_private.syms (virstring.h): Export them. * tests/virstringtest.c (testStringToLong): Test them. Signed-off-by: Eric Blake --- src/libvirt_private.syms | 3 ++ src/util/virstring.c | 80 ++++++++++++++++++++++++++++++++++++++-- src/util/virstring.h | 14 ++++++- tests/virstringtest.c | 23 ++++++++++++ 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4cfaefc044..cc3ae2c290 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1899,8 +1899,11 @@ virStrToLong_i; virStrToLong_l; virStrToLong_ll; virStrToLong_ui; +virStrToLong_uip; virStrToLong_ul; virStrToLong_ull; +virStrToLong_ullp; +virStrToLong_ulp; virTrimSpaces; virVasprintfInternal; diff --git a/src/util/virstring.c b/src/util/virstring.c index b5fc6380f6..7a8430e847 100644 --- a/src/util/virstring.c +++ b/src/util/virstring.c @@ -211,7 +211,9 @@ virStrToLong_i(char const *s, char **end_ptr, int base, int *result) return 0; } -/* Just like virStrToLong_i, above, but produce an "unsigned int" value. */ +/* Just like virStrToLong_i, above, but produce an "unsigned int" + * value. This version allows twos-complement wraparound of negative + * numbers. */ int virStrToLong_ui(char const *s, char **end_ptr, int base, unsigned int *result) { @@ -242,6 +244,27 @@ virStrToLong_ui(char const *s, char **end_ptr, int base, unsigned int *result) return 0; } +/* Just like virStrToLong_i, above, but produce an "unsigned int" + * value. This version rejects any negative signs. */ +int +virStrToLong_uip(char const *s, char **end_ptr, int base, unsigned int *result) +{ + unsigned long int val; + char *p; + bool err = false; + + errno = 0; + val = strtoul(s, &p, base); /* exempt from syntax-check */ + err = (memchr(s, '-', p - s) || + errno || (!end_ptr && *p) || p == s || (unsigned int) val != val); + if (end_ptr) + *end_ptr = p; + if (err) + return -1; + *result = val; + return 0; +} + /* Just like virStrToLong_i, above, but produce a "long" value. */ int virStrToLong_l(char const *s, char **end_ptr, int base, long *result) @@ -261,7 +284,9 @@ virStrToLong_l(char const *s, char **end_ptr, int base, long *result) return 0; } -/* Just like virStrToLong_i, above, but produce an "unsigned long" value. */ +/* Just like virStrToLong_i, above, but produce an "unsigned long" + * value. This version allows twos-complement wraparound of negative + * numbers. */ int virStrToLong_ul(char const *s, char **end_ptr, int base, unsigned long *result) { @@ -280,6 +305,28 @@ virStrToLong_ul(char const *s, char **end_ptr, int base, unsigned long *result) return 0; } +/* Just like virStrToLong_i, above, but produce an "unsigned long" + * value. This version rejects any negative signs. */ +int +virStrToLong_ulp(char const *s, char **end_ptr, int base, + unsigned long *result) +{ + unsigned long int val; + char *p; + int err; + + errno = 0; + val = strtoul(s, &p, base); /* exempt from syntax-check */ + err = (memchr(s, '-', p - s) || + errno || (!end_ptr && *p) || p == s); + if (end_ptr) + *end_ptr = p; + if (err) + return -1; + *result = val; + return 0; +} + /* Just like virStrToLong_i, above, but produce a "long long" value. */ int virStrToLong_ll(char const *s, char **end_ptr, int base, long long *result) @@ -299,9 +346,12 @@ virStrToLong_ll(char const *s, char **end_ptr, int base, long long *result) return 0; } -/* Just like virStrToLong_i, above, but produce an "unsigned long long" value. */ +/* Just like virStrToLong_i, above, but produce an "unsigned long + * long" value. This version allows twos-complement wraparound of + * negative numbers. */ int -virStrToLong_ull(char const *s, char **end_ptr, int base, unsigned long long *result) +virStrToLong_ull(char const *s, char **end_ptr, int base, + unsigned long long *result) { unsigned long long val; char *p; @@ -318,6 +368,28 @@ virStrToLong_ull(char const *s, char **end_ptr, int base, unsigned long long *re return 0; } +/* Just like virStrToLong_i, above, but produce an "unsigned long + * long" value. This version rejects any negative signs. */ +int +virStrToLong_ullp(char const *s, char **end_ptr, int base, + unsigned long long *result) +{ + unsigned long long val; + char *p; + int err; + + errno = 0; + val = strtoull(s, &p, base); /* exempt from syntax-check */ + err = (memchr(s, '-', p - s) || + errno || (!end_ptr && *p) || p == s); + if (end_ptr) + *end_ptr = p; + if (err) + return -1; + *result = val; + return 0; +} + int virStrToDouble(char const *s, char **end_ptr, diff --git a/src/util/virstring.h b/src/util/virstring.h index 5b77581aaa..1ed104682c 100644 --- a/src/util/virstring.h +++ b/src/util/virstring.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2012 Red Hat, Inc. + * Copyright (C) 2007-2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -50,6 +50,10 @@ int virStrToLong_ui(char const *s, char **end_ptr, int base, unsigned int *result); +int virStrToLong_uip(char const *s, + char **end_ptr, + int base, + unsigned int *result); int virStrToLong_l(char const *s, char **end_ptr, int base, @@ -58,6 +62,10 @@ int virStrToLong_ul(char const *s, char **end_ptr, int base, unsigned long *result); +int virStrToLong_ulp(char const *s, + char **end_ptr, + int base, + unsigned long *result); int virStrToLong_ll(char const *s, char **end_ptr, int base, @@ -66,6 +74,10 @@ int virStrToLong_ull(char const *s, char **end_ptr, int base, unsigned long long *result); +int virStrToLong_ullp(char const *s, + char **end_ptr, + int base, + unsigned long long *result); int virStrToDouble(char const *s, char **end_ptr, double *result); diff --git a/tests/virstringtest.c b/tests/virstringtest.c index 0380df136e..1e330f9593 100644 --- a/tests/virstringtest.c +++ b/tests/virstringtest.c @@ -408,6 +408,13 @@ testStringToLong(const void *opaque) char *end; long l; unsigned long ul; + bool negative; + + if (data->suffix) + negative = !!memchr(data->str, '-', + strlen(data->str) - strlen(data->suffix)); + else + negative = !!strchr(data->str, '-'); #define TEST_ONE(Str, Suff, Type, Fn, Fmt, Exp, Exp_ret) \ do { \ @@ -441,6 +448,11 @@ testStringToLong(const void *opaque) data->si, data->si_ret); TEST_ONE(data->str, data->suffix, unsigned int, ui, "%u", data->ui, data->ui_ret); + if (negative) + TEST_ONE(data->str, data->suffix, unsigned int, uip, "%u", 0U, -1); + else + TEST_ONE(data->str, data->suffix, unsigned int, uip, "%u", + data->ui, data->ui_ret); /* We hate adding new API with 'long', and prefer 'int' or 'long * long' instead, since platform-specific results are evil */ @@ -450,11 +462,22 @@ testStringToLong(const void *opaque) ul = (sizeof(int) == sizeof(long)) ? data->ui : data->ull; TEST_ONE(data->str, data->suffix, unsigned long, ul, "%lu", ul, (sizeof(int) == sizeof(long)) ? data->ui_ret : data->ull_ret); + if (negative) + TEST_ONE(data->str, data->suffix, unsigned long, ulp, "%lu", 0UL, -1); + else + TEST_ONE(data->str, data->suffix, unsigned long, ulp, "%lu", ul, + (sizeof(int) == sizeof(long)) ? data->ui_ret : data->ull_ret); TEST_ONE(data->str, data->suffix, long long, ll, "%lld", data->ll, data->ll_ret); TEST_ONE(data->str, data->suffix, unsigned long long, ull, "%llu", data->ull, data->ull_ret); + if (negative) + TEST_ONE(data->str, data->suffix, unsigned long long, ullp, "%llu", + 0ULL, -1); + else + TEST_ONE(data->str, data->suffix, unsigned long long, ullp, "%llu", + data->ull, data->ull_ret); #undef TEST_ONE -- GitLab