* [PATCH] Add parse_integer() (replacement for simple_strto*())
@ 2015-04-09 15:26 Alexey Dobriyan
2015-04-09 15:28 ` Alexey Dobriyan
2015-04-09 20:02 ` Andrew Morton
0 siblings, 2 replies; 6+ messages in thread
From: Alexey Dobriyan @ 2015-04-09 15:26 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel
kstrto*() and kstrto*_from_user() family of functions were added
help with parsing one integer written as string to proc/sysfs/debugfs
files and pass it elsewhere. But they have a limitation: string passed
must end with \0 or \n\0. There are enough places where kstrto*()
functions can't be used because of this limitation. Trivial example:
parse "%u.%u".
Currently the only way to parse everything is simple_strto*() family of
functions. But they are suboptimal:
* they do not detect overflow,
* there are only 2(4) of them -- long and "long long" version.
This leads to silent truncation in the most simple case:
val = strtoul(s, NULL, 0);
* half of the people think that "char **endp" argument is necessary and
add unnecessary variable.
OpenBSD people, fed up with how complex correct integer parsing is, added
strtonum(3) to fixup for deficiencies of libc-style integer parsing:
http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/strtonum.3?query=strtonum&arch=i386
It'd be OK to copy that but it relies on "errno" and fixed strings as
error-reporting channel which I think is not OK for kernel.
strtonum() also doesn't report number of characted consumed.
What to do?
Enter parse_integer().
int parse_integer(const char *s, unsigned int base, T *val);
Rationale:
* parse_integer() is exactly 1 (one) interface not 2 or many,
one for each type.
* parse_integer() reports -E errors reliably in a canonical kernel way:
rv = parse_integer(str, 10, &val);
if (rv < 0)
return rv;
* parse_integer() writes result only if there were no errors, at least
one digit was consumed.
* parse_integer doesn't mix error reporting channel and value channel,
it does mix error and number-of-character-consumed channel, though.
* parse_integer() reports number of characters consumed, makes parsing
multiple values easy:
rv = parse_integer(str, 0, &val1);
if (rv < 0)
return rv;
s += rv;
if (s != '.')
return -EINVAL;
rv = parse_integer(str, 0, &val2);
if (rv < 0)
return rv;
if (str[rv] != '\0')
return -EINVAL;
There are several deficiencies in parse_integer() compared to strto*():
* can't be used in initializers:
const T x = strtoul();
* can't be used with bitfields,
* can't be used in experessions:
x = strtol() * 1024;
x = y = strtol() * 1024;
x += strtol();
In defense of parse_integer() all I can say, is that using strtol() in
expression or initializer promotes no error checking and thus probably
should not be encouraged in C, language with no built-in error checking
anyway.
The amount of "x = y = strtol()" expressions in kernel is very small.
The amount of direct usage in expression is not small, but can be
counted as an acceptable loss.
Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
---
include/linux/kernel.h | 72 +++++++++++++++++++
lib/Makefile | 1
lib/kstrtox.c | 27 +------
lib/parse-integer.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 257 insertions(+), 23 deletions(-)
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -251,6 +251,78 @@ void do_exit(long error_code)
void complete_and_exit(struct completion *, long)
__noreturn;
+/*
+ * int parse_integer(const char *s, unsigned int base, T *val);
+ *
+ * Convert integer string representation to an integer.
+ * If an integer doesn't fit into specified type, -E is returned.
+ *
+ * Conversion to signed integer accepts sign "-".
+ * Conversion to an unsigned integer doesn't accept sign "-".
+ *
+ * Radix 0 means autodetection: leading "0x" implies radix 16,
+ * leading "0" implies radix 8, otherwise radix is 10.
+ * Autodetection hints work after optional sign, but not before.
+ *
+ * If -E is returned, result is not touched.
+ *
+ * "T = char" case is not supported because -f{un,}signed-char can silently
+ * change range of accepted values.
+ */
+#define parse_integer(s, base, val) \
+({ \
+ const char *_s = (s); \
+ unsigned int _base = (base); \
+ typeof(val) _val = (val); \
+ \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), signed char *), \
+ _parse_integer_sc(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), unsigned char *), \
+ _parse_integer_uc(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), short *), \
+ _parse_integer_s(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), unsigned short *), \
+ _parse_integer_us(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), int *), \
+ _parse_integer_i(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), unsigned int *), \
+ _parse_integer_u(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), long *) && BITS_PER_LONG == 32,\
+ _parse_integer_i(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), long *) && BITS_PER_LONG == 64,\
+ _parse_integer_ll(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), unsigned long *) && BITS_PER_LONG == 32,\
+ _parse_integer_u(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), unsigned long *) && BITS_PER_LONG == 64,\
+ _parse_integer_ull(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), long long *), \
+ _parse_integer_ll(_s, _base, (void *)_val), \
+ __builtin_choose_expr( \
+ __builtin_types_compatible_p(typeof(_val), unsigned long long *),\
+ _parse_integer_ull(_s, _base, (void *)_val), \
+ _parse_integer_link_time_error())))))))))))); \
+})
+int _parse_integer_sc(const char *s, unsigned int base, signed char *val);
+int _parse_integer_uc(const char *s, unsigned int base, unsigned char *val);
+int _parse_integer_s(const char *s, unsigned int base, short *val);
+int _parse_integer_us(const char *s, unsigned int base, unsigned short *val);
+int _parse_integer_i(const char *s, unsigned int base, int *val);
+int _parse_integer_u(const char *s, unsigned int base, unsigned int *val);
+int _parse_integer_ll(const char *s, unsigned int base, long long *val);
+int _parse_integer_ull(const char *s, unsigned int base, unsigned long long *val);
+void _parse_integer_link_time_error(void);
+
/* Internal, do not use. */
int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res);
int __must_check _kstrtol(const char *s, unsigned int base, long *res);
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o
obj-y += hexdump.o
obj-$(CONFIG_TEST_HEXDUMP) += test-hexdump.o
obj-y += kstrtox.o
+obj-y += parse-integer.o
obj-$(CONFIG_TEST_BPF) += test_bpf.o
obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o
obj-$(CONFIG_TEST_KASAN) += test_kasan.o
--- a/lib/kstrtox.c
+++ b/lib/kstrtox.c
@@ -20,22 +20,6 @@
#include <asm/uaccess.h>
#include "kstrtox.h"
-const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
-{
- if (*base == 0) {
- if (s[0] == '0') {
- if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
- *base = 16;
- else
- *base = 8;
- } else
- *base = 10;
- }
- if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
- s += 2;
- return s;
-}
-
/*
* Convert non-negative integer string representation in explicitly given radix
* to an integer.
@@ -86,14 +70,11 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long
static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
unsigned long long _res;
- unsigned int rv;
+ int rv;
- s = _parse_integer_fixup_radix(s, &base);
- rv = _parse_integer(s, base, &_res);
- if (rv & KSTRTOX_OVERFLOW)
- return -ERANGE;
- if (rv == 0)
- return -EINVAL;
+ rv = parse_integer(s, base, &_res);
+ if (rv < 0)
+ return rv;
s += rv;
if (*s == '\n')
s++;
--- /dev/null
+++ b/lib/parse-integer.c
@@ -0,0 +1,180 @@
+/*
+ * See parse_integer().
+ *
+ * Individual dispatch functions in this file aren't supposed to be used
+ * directly and thus aren't advertised and documented despited being exported.
+ *
+ * Do not use any function in this file for any reason.
+ */
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <asm/bug.h>
+#include "kstrtox.h"
+
+const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
+{
+ if (*base == 0) {
+ if (s[0] == '0') {
+ if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
+ *base = 16;
+ else
+ *base = 8;
+ } else
+ *base = 10;
+ }
+ if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
+ s += 2;
+ BUG_ON(*base < 2 || *base > 16);
+ return s;
+}
+
+int _parse_integer_ull(const char *s, unsigned int base, unsigned long long *val)
+{
+ const char *s0 = s, *sd;
+ unsigned long long acc;
+
+ s = sd = _parse_integer_fixup_radix(s0, &base);
+ acc = 0;
+ while (*s != '\0') {
+ unsigned int d;
+
+ if ('0' <= *s && *s <= '9')
+ d = *s - '0';
+ else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
+ d = _tolower(*s) - 'a' + 10;
+ else
+ break;
+ if (d >= base)
+ break;
+ /* Overflow can't happen early enough. */
+ if ((acc >> 60) && acc > div_u64(ULLONG_MAX - d, base))
+ return -ERANGE;
+ acc = acc * base + d;
+ s++;
+ }
+ /* At least one digit has to be converted. */
+ if (s == sd)
+ return -EINVAL;
+ *val = acc;
+ /* Radix 1 is not supported otherwise returned length can overflow. */
+ return s - s0;
+}
+EXPORT_SYMBOL(_parse_integer_ull);
+
+int _parse_integer_ll(const char *s, unsigned int base, long long *val)
+{
+ unsigned long long tmp;
+ int rv;
+
+ if (*s == '-') {
+ rv = _parse_integer_ull(s + 1, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if ((long long)-tmp >= 0)
+ return -ERANGE;
+ *val = -tmp;
+ return rv + 1;
+ } else {
+ rv = _parse_integer_ull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if ((long long)tmp < 0)
+ return -ERANGE;
+ *val = tmp;
+ return rv;
+ }
+}
+EXPORT_SYMBOL(_parse_integer_ll);
+
+int _parse_integer_u(const char *s, unsigned int base, unsigned int *val)
+{
+ unsigned long long tmp;
+ int rv;
+
+ rv = _parse_integer_ull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (unsigned int)tmp)
+ return -ERANGE;
+ *val = tmp;
+ return rv;
+}
+EXPORT_SYMBOL(_parse_integer_u);
+
+int _parse_integer_i(const char *s, unsigned int base, int *val)
+{
+ long long tmp;
+ int rv;
+
+ rv = _parse_integer_ll(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (int)tmp)
+ return -ERANGE;
+ *val = tmp;
+ return rv;
+}
+EXPORT_SYMBOL(_parse_integer_i);
+
+int _parse_integer_us(const char *s, unsigned int base, unsigned short *val)
+{
+ unsigned long long tmp;
+ int rv;
+
+ rv = _parse_integer_ull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (unsigned short)tmp)
+ return -ERANGE;
+ *val = tmp;
+ return rv;
+}
+EXPORT_SYMBOL(_parse_integer_us);
+
+int _parse_integer_s(const char *s, unsigned int base, short *val)
+{
+ long long tmp;
+ int rv;
+
+ rv = _parse_integer_ll(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (short)tmp)
+ return -ERANGE;
+ *val = tmp;
+ return rv;
+}
+EXPORT_SYMBOL(_parse_integer_s);
+
+int _parse_integer_uc(const char *s, unsigned int base, unsigned char *val)
+{
+ unsigned long long tmp;
+ int rv;
+
+ rv = _parse_integer_ull(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (unsigned char)tmp)
+ return -ERANGE;
+ *val = tmp;
+ return rv;
+}
+EXPORT_SYMBOL(_parse_integer_uc);
+
+int _parse_integer_sc(const char *s, unsigned int base, signed char *val)
+{
+ long long tmp;
+ int rv;
+
+ rv = _parse_integer_ll(s, base, &tmp);
+ if (rv < 0)
+ return rv;
+ if (tmp != (signed char)tmp)
+ return -ERANGE;
+ *val = tmp;
+ return rv;
+}
+EXPORT_SYMBOL(_parse_integer_sc);
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [PATCH] Add parse_integer() (replacement for simple_strto*()) 2015-04-09 15:26 [PATCH] Add parse_integer() (replacement for simple_strto*()) Alexey Dobriyan @ 2015-04-09 15:28 ` Alexey Dobriyan 2015-04-09 20:02 ` Andrew Morton 1 sibling, 0 replies; 6+ messages in thread From: Alexey Dobriyan @ 2015-04-09 15:28 UTC (permalink / raw) To: akpm; +Cc: linux-kernel On Thu, Apr 09, 2015 at 06:26:14PM +0300, Alexey Dobriyan wrote: > int parse_integer(const char *s, unsigned int base, T *val); This is an example how conversion looks like (for mm/ directory): --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -4187,20 +4187,23 @@ static ssize_t memcg_write_event_control(struct kernfs_open_file *of, struct fd efile; struct fd cfile; const char *name; - char *endp; int ret; buf = strstrip(buf); - efd = simple_strtoul(buf, &endp, 10); - if (*endp != ' ') + ret = parse_integer(buf, 10, &efd); + if (ret < 0) + return ret; + if (buf[ret] != ' ') return -EINVAL; - buf = endp + 1; + buf += ret + 1; - cfd = simple_strtoul(buf, &endp, 10); - if ((*endp != ' ') && (*endp != '\0')) + ret = parse_integer(buf, 10, &cfd); + if (ret < 0) + return ret; + if (buf[ret] != ' ' && buf[ret] != '\0') return -EINVAL; - buf = endp + 1; + buf += ret + 1; event = kzalloc(sizeof(*event), GFP_KERNEL); if (!event) --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5977,7 +5977,7 @@ static int __init set_hashdist(char *str) { if (!str) return 0; - hashdist = simple_strtoul(str, &str, 0); + parse_integer(str, 0, &hashdist); return 1; } __setup("hashdist=", set_hashdist); --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2742,6 +2742,7 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, struct mempolicy *mpol = NULL; uid_t uid; gid_t gid; + int rv; while (options != NULL) { this_char = options; @@ -2795,14 +2796,15 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, } else if (!strcmp(this_char,"mode")) { if (remount) continue; - sbinfo->mode = simple_strtoul(value, &rest, 8) & 07777; - if (*rest) + rv = parse_integer(value, 8, &sbinfo->mode); + if (rv < 0 || value[rv]) goto bad_val; + sbinfo->mode &= 07777; } else if (!strcmp(this_char,"uid")) { if (remount) continue; - uid = simple_strtoul(value, &rest, 0); - if (*rest) + rv = parse_integer(value, 0, &uid); + if (rv < 0 || value[rv]) goto bad_val; sbinfo->uid = make_kuid(current_user_ns(), uid); if (!uid_valid(sbinfo->uid)) @@ -2810,8 +2812,8 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo, } else if (!strcmp(this_char,"gid")) { if (remount) continue; - gid = simple_strtoul(value, &rest, 0); - if (*rest) + rv = parse_integer(value, 0, &gid); + if (rv < 0 || value[rv]) goto bad_val; sbinfo->gid = make_kgid(current_user_ns(), gid); if (!gid_valid(sbinfo->gid)) ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] Add parse_integer() (replacement for simple_strto*()) 2015-04-09 15:26 [PATCH] Add parse_integer() (replacement for simple_strto*()) Alexey Dobriyan 2015-04-09 15:28 ` Alexey Dobriyan @ 2015-04-09 20:02 ` Andrew Morton 2015-04-11 15:36 ` Alexey Dobriyan 1 sibling, 1 reply; 6+ messages in thread From: Andrew Morton @ 2015-04-09 20:02 UTC (permalink / raw) To: Alexey Dobriyan; +Cc: linux-kernel On Thu, 9 Apr 2015 18:26:14 +0300 Alexey Dobriyan <adobriyan@gmail.com> wrote: > kstrto*() and kstrto*_from_user() family of functions were added > help with parsing one integer written as string to proc/sysfs/debugfs > files and pass it elsewhere. But they have a limitation: string passed > must end with \0 or \n\0. There are enough places where kstrto*() > functions can't be used because of this limitation. Trivial example: > parse "%u.%u". > > ... > > include/linux/kernel.h | 72 +++++++++++++++++++ > lib/Makefile | 1 > lib/kstrtox.c | 27 +------ > lib/parse-integer.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 257 insertions(+), 23 deletions(-) That's a lot of code for something which is almost the same as kstrtofoo(). Can we hack up _kstrtoull() to optionally provide the new behaviour? We could use the top bit of `base' to select the behaviour. Something like --- a/lib/kstrtox.c~a +++ a/lib/kstrtox.c @@ -87,19 +87,23 @@ static int _kstrtoull(const char *s, uns { unsigned long long _res; unsigned int rv; + const char *cursor; + const unsigned int __base = base & 0x7fffffff; - s = _parse_integer_fixup_radix(s, &base); - rv = _parse_integer(s, base, &_res); + cursor = _parse_integer_fixup_radix(s, &__base); + rv = _parse_integer(cursor, __base, &_res); if (rv & KSTRTOX_OVERFLOW) return -ERANGE; if (rv == 0) return -EINVAL; - s += rv; - if (*s == '\n') - s++; - if (*s) - return -EINVAL; + cursor += rv; *res = _res; + if (base & 0x80000000) + return cursor - s; + if (*cursor == '\n') + cursor++; + if (*cursor) + return -EINVAL; return 0; } then add a bunch of wrappers? ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] Add parse_integer() (replacement for simple_strto*()) 2015-04-09 20:02 ` Andrew Morton @ 2015-04-11 15:36 ` Alexey Dobriyan 2015-04-11 15:38 ` [PATCH 1/2] " Alexey Dobriyan 0 siblings, 1 reply; 6+ messages in thread From: Alexey Dobriyan @ 2015-04-11 15:36 UTC (permalink / raw) To: Andrew Morton; +Cc: linux-kernel On Thu, Apr 09, 2015 at 01:02:47PM -0700, Andrew Morton wrote: > On Thu, 9 Apr 2015 18:26:14 +0300 Alexey Dobriyan <adobriyan@gmail.com> wrote: > > > kstrto*() and kstrto*_from_user() family of functions were added > > help with parsing one integer written as string to proc/sysfs/debugfs > > files and pass it elsewhere. But they have a limitation: string passed > > must end with \0 or \n\0. There are enough places where kstrto*() > > functions can't be used because of this limitation. Trivial example: > > parse "%u.%u". > > > > ... > > > > include/linux/kernel.h | 72 +++++++++++++++++++ > > lib/Makefile | 1 > > lib/kstrtox.c | 27 +------ > > lib/parse-integer.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++++ > > 4 files changed, 257 insertions(+), 23 deletions(-) > > That's a lot of code for something which is almost the same as > kstrtofoo(). > > Can we hack up _kstrtoull() to optionally provide the new behaviour? > We could use the top bit of `base' to select the behaviour. Hmm, OK. I did it the opposite way, though. parse_integer() becomes core interface, kstrto*() become wrappers. Alexey ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/2] Add parse_integer() (replacement for simple_strto*()) 2015-04-11 15:36 ` Alexey Dobriyan @ 2015-04-11 15:38 ` Alexey Dobriyan 2015-04-11 15:43 ` [PATCH 2/2] parse_integer: rewrite kstrto*() Alexey Dobriyan 0 siblings, 1 reply; 6+ messages in thread From: Alexey Dobriyan @ 2015-04-11 15:38 UTC (permalink / raw) To: Andrew Morton; +Cc: linux-kernel, adobriyan kstrto*() and kstrto*_from_user() family of functions were added help with parsing one integer written as string to proc/sysfs/debugfs files and pass it elsewhere. But they have a limitation: string passed must end with \0 or \n\0. There are enough places where kstrto*() functions can't be used because of this limitation. Trivial example: parse "%u.%u". Currently the only way to parse everything is simple_strto*() family of functions. But they are suboptimal: * they do not detect overflow, * there are only 2(4) of them -- long and "long long" version. This leads to silent truncation in the most simple case: val = strtoul(s, NULL, 0); * half of the people think that "char **endp" argument is necessary and add unnecessary variable. OpenBSD people, fed up with how complex correct integer parsing is, added strtonum(3) to fixup for deficiencies of libc-style integer parsing: http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man3/strtonum.3?query=strtonum&arch=i386 It'd be OK to copy that but it relies on "errno" and fixed strings as error-reporting channel which I think is not OK for kernel. strtonum() also doesn't report number of characted consumed. What to do? Enter parse_integer(). int parse_integer(const char *s, unsigned int base, T *val); Rationale: * parse_integer() is exactly 1 (one) interface not 2 or many, one for each type. * parse_integer() reports -E errors reliably in a canonical kernel way: rv = parse_integer(str, 10, &val); if (rv < 0) return rv; * parse_integer() writes result only if there were no errors, at least one digit was consumed. * parse_integer doesn't mix error reporting channel and value channel, it does mix error and number-of-character-consumed channel, though. * parse_integer() reports number of characters consumed, makes parsing multiple values easy: rv = parse_integer(str, 0, &val1); if (rv < 0) return rv; s += rv; if (s != '.') return -EINVAL; rv = parse_integer(str, 0, &val2); if (rv < 0) return rv; if (str[rv] != '\0') return -EINVAL; There are several deficiencies in parse_integer() compared to strto*(): * can't be used in initializers: const T x = strtoul(); * can't be used with bitfields, * can't be used in experessions: x = strtol() * 1024; x = y = strtol() * 1024; x += strtol(); In defense of parse_integer() all I can say, is that using strtol() in expression or initializer promotes no error checking and thus probably should not be encouraged in C, language with no built-in error checking anyway. The amount of "x = y = strtol()" expressions in kernel is very small. The amount of direct usage in expression is not small, but can be counted as an acceptable loss. Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> --- include/linux/kernel.h | 3 include/linux/parse-integer.h | 79 ++++++++++++++++ lib/Makefile | 1 lib/kstrtox.c | 76 ++++----------- lib/kstrtox.h | 1 lib/parse-integer.c | 203 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 307 insertions(+), 56 deletions(-) --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -10,6 +10,7 @@ #include <linux/bitops.h> #include <linux/log2.h> #include <linux/typecheck.h> +#include <linux/parse-integer.h> #include <linux/printk.h> #include <linux/dynamic_debug.h> #include <asm/byteorder.h> @@ -375,7 +376,7 @@ static inline int __must_check kstrtos32_from_user(const char __user *s, size_t return kstrtoint_from_user(s, count, base, res); } -/* Obsolete, do not use. Use kstrto<foo> instead */ +/* Obsolete, do not use. Use parse_integer(), kstrto*() or kstrto*_from_user(). */ extern unsigned long simple_strtoul(const char *,char **,unsigned int); extern long simple_strtol(const char *,char **,unsigned int); --- /dev/null +++ b/include/linux/parse-integer.h @@ -0,0 +1,79 @@ +#ifndef _PARSE_INTEGER_H +#define _PARSE_INTEGER_H +#include <asm/bitsperlong.h> + +/* + * int parse_integer(const char *s, unsigned int base, T *val); + * + * Convert integer string representation to an integer. + * Range of accepted values equals to that of type T. + * + * Conversion to unsigned integer accepts sign "+". + * Conversion to signed integer accepts sign "+" and sign "-". + * + * Radix 0 means autodetection: leading "0x" implies radix 16, + * leading "0" implies radix 8, otherwise radix is 10. + * Autodetection hint works after optional sign, but not before. + * + * Return number of characters parsed or -E. + * + * "T = char" case is not supported because -f{un,}signed-char can silently + * change range of accepted values. + */ +#define parse_integer(s, base, val) \ +({ \ + const char *_s = (s); \ + unsigned int _base = (base); \ + typeof(val) _val = (val); \ + \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), signed char *), \ + _parse_integer_sc(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), unsigned char *), \ + _parse_integer_uc(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), short *), \ + _parse_integer_s(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), unsigned short *), \ + _parse_integer_us(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), int *), \ + _parse_integer_i(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), unsigned int *), \ + _parse_integer_u(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), long *) && BITS_PER_LONG == 32,\ + _parse_integer_i(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), long *) && BITS_PER_LONG == 64,\ + _parse_integer_ll(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), unsigned long *) && BITS_PER_LONG == 32,\ + _parse_integer_u(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), unsigned long *) && BITS_PER_LONG == 64,\ + _parse_integer_ull(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), long long *), \ + _parse_integer_ll(_s, _base, (void *)_val), \ + __builtin_choose_expr( \ + __builtin_types_compatible_p(typeof(_val), unsigned long long *),\ + _parse_integer_ull(_s, _base, (void *)_val), \ + _parse_integer_link_time_error())))))))))))); \ +}) +int _parse_integer_sc(const char *s, unsigned int base, signed char *val); +int _parse_integer_uc(const char *s, unsigned int base, unsigned char *val); +int _parse_integer_s(const char *s, unsigned int base, short *val); +int _parse_integer_us(const char *s, unsigned int base, unsigned short *val); +int _parse_integer_i(const char *s, unsigned int base, int *val); +int _parse_integer_u(const char *s, unsigned int base, unsigned int *val); +int _parse_integer_ll(const char *s, unsigned int base, long long *val); +int _parse_integer_ull(const char *s, unsigned int base, unsigned long long *val); +void _parse_integer_link_time_error(void); + +/* internal, do not use */ +const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); +#endif --- a/lib/Makefile +++ b/lib/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o obj-y += hexdump.o obj-$(CONFIG_TEST_HEXDUMP) += test-hexdump.o obj-y += kstrtox.o +obj-y += parse-integer.o obj-$(CONFIG_TEST_BPF) += test_bpf.o obj-$(CONFIG_TEST_FIRMWARE) += test_firmware.o obj-$(CONFIG_TEST_KASAN) += test_kasan.o --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -20,22 +20,6 @@ #include <asm/uaccess.h> #include "kstrtox.h" -const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) -{ - if (*base == 0) { - if (s[0] == '0') { - if (_tolower(s[1]) == 'x' && isxdigit(s[2])) - *base = 16; - else - *base = 8; - } else - *base = 10; - } - if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') - s += 2; - return s; -} - /* * Convert non-negative integer string representation in explicitly given radix * to an integer. @@ -83,26 +67,6 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long return rv; } -static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) -{ - unsigned long long _res; - unsigned int rv; - - s = _parse_integer_fixup_radix(s, &base); - rv = _parse_integer(s, base, &_res); - if (rv & KSTRTOX_OVERFLOW) - return -ERANGE; - if (rv == 0) - return -EINVAL; - s += rv; - if (*s == '\n') - s++; - if (*s) - return -EINVAL; - *res = _res; - return 0; -} - /** * kstrtoull - convert a string to an unsigned long long * @s: The start of the string. The string must be null-terminated, and may also @@ -121,9 +85,19 @@ static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res) */ int kstrtoull(const char *s, unsigned int base, unsigned long long *res) { - if (s[0] == '+') + unsigned long long _res; + int rv; + + rv = parse_integer(s, base, &_res); + if (rv < 0) + return rv; + s += rv; + if (*s == '\n') s++; - return _kstrtoull(s, base, res); + if (*s) + return -EINVAL; + *res = _res; + return 0; } EXPORT_SYMBOL(kstrtoull); @@ -145,24 +119,18 @@ EXPORT_SYMBOL(kstrtoull); */ int kstrtoll(const char *s, unsigned int base, long long *res) { - unsigned long long tmp; + long long _res; int rv; - if (s[0] == '-') { - rv = _kstrtoull(s + 1, base, &tmp); - if (rv < 0) - return rv; - if ((long long)(-tmp) >= 0) - return -ERANGE; - *res = -tmp; - } else { - rv = kstrtoull(s, base, &tmp); - if (rv < 0) - return rv; - if ((long long)tmp < 0) - return -ERANGE; - *res = tmp; - } + rv = parse_integer(s, base, &_res); + if (rv < 0) + return rv; + s += rv; + if (*s == '\n') + s++; + if (*s) + return -EINVAL; + *res = _res; return 0; } EXPORT_SYMBOL(kstrtoll); --- a/lib/kstrtox.h +++ b/lib/kstrtox.h @@ -2,7 +2,6 @@ #define _LIB_KSTRTOX_H #define KSTRTOX_OVERFLOW (1U << 31) -const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res); #endif --- /dev/null +++ b/lib/parse-integer.c @@ -0,0 +1,203 @@ +/* + * See parse_integer(). + * + * Individual dispatch functions in this file aren't supposed to be used + * directly and thus aren't advertised and documented despited being exported. + * + * Do not use any function in this file for any reason. + */ +#include <linux/ctype.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/math64.h> +#include <linux/parse-integer.h> +#include <asm/bug.h> + +const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) +{ + if (*base == 0) { + if (s[0] == '0') { + if (_tolower(s[1]) == 'x' && isxdigit(s[2])) + *base = 16; + else + *base = 8; + } else + *base = 10; + } + if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') + s += 2; + BUG_ON(*base < 2 || *base > 16); + return s; +} + +static int __parse_integer(const char *s, unsigned int base, unsigned long long *val) +{ + const char *s0 = s, *sd; + unsigned long long acc; + + s = sd = _parse_integer_fixup_radix(s0, &base); + acc = 0; + while (*s != '\0') { + unsigned int d; + + if ('0' <= *s && *s <= '9') + d = *s - '0'; + else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f') + d = _tolower(*s) - 'a' + 10; + else + break; + if (d >= base) + break; + /* Overflow can't happen early enough. */ + if ((acc >> 60) && acc > div_u64(ULLONG_MAX - d, base)) + return -ERANGE; + acc = acc * base + d; + s++; + } + /* At least one digit has to be converted. */ + if (s == sd) + return -EINVAL; + *val = acc; + /* Radix 1 is not supported otherwise returned length can overflow. */ + return s - s0; +} + +int _parse_integer_ull(const char *s, unsigned int base, unsigned long long *val) +{ + int rv; + + if (*s == '-') { + return -EINVAL; + } else if (*s == '+') { + rv = __parse_integer(s + 1, base, val); + if (rv < 0) + return rv; + return rv + 1; + } else + return __parse_integer(s, base, val); +} +EXPORT_SYMBOL(_parse_integer_ull); + +int _parse_integer_ll(const char *s, unsigned int base, long long *val) +{ + unsigned long long tmp; + int rv; + + if (*s == '-') { + rv = __parse_integer(s + 1, base, &tmp); + if (rv < 0) + return rv; + if ((long long)-tmp >= 0) + return -ERANGE; + *val = -tmp; + return rv + 1; + } else if (*s == '+') { + rv = __parse_integer(s + 1, base, &tmp); + if (rv < 0) + return rv; + if ((long long)tmp < 0) + return -ERANGE; + *val = tmp; + return rv + 1; + } else { + rv = __parse_integer(s, base, &tmp); + if (rv < 0) + return rv; + if ((long long)tmp < 0) + return -ERANGE; + *val = tmp; + return rv; + } +} +EXPORT_SYMBOL(_parse_integer_ll); + +int _parse_integer_u(const char *s, unsigned int base, unsigned int *val) +{ + unsigned long long tmp; + int rv; + + rv = _parse_integer_ull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned int)tmp) + return -ERANGE; + *val = tmp; + return rv; +} +EXPORT_SYMBOL(_parse_integer_u); + +int _parse_integer_i(const char *s, unsigned int base, int *val) +{ + long long tmp; + int rv; + + rv = _parse_integer_ll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (int)tmp) + return -ERANGE; + *val = tmp; + return rv; +} +EXPORT_SYMBOL(_parse_integer_i); + +int _parse_integer_us(const char *s, unsigned int base, unsigned short *val) +{ + unsigned long long tmp; + int rv; + + rv = _parse_integer_ull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned short)tmp) + return -ERANGE; + *val = tmp; + return rv; +} +EXPORT_SYMBOL(_parse_integer_us); + +int _parse_integer_s(const char *s, unsigned int base, short *val) +{ + long long tmp; + int rv; + + rv = _parse_integer_ll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (short)tmp) + return -ERANGE; + *val = tmp; + return rv; +} +EXPORT_SYMBOL(_parse_integer_s); + +int _parse_integer_uc(const char *s, unsigned int base, unsigned char *val) +{ + unsigned long long tmp; + int rv; + + rv = _parse_integer_ull(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (unsigned char)tmp) + return -ERANGE; + *val = tmp; + return rv; +} +EXPORT_SYMBOL(_parse_integer_uc); + +int _parse_integer_sc(const char *s, unsigned int base, signed char *val) +{ + long long tmp; + int rv; + + rv = _parse_integer_ll(s, base, &tmp); + if (rv < 0) + return rv; + if (tmp != (signed char)tmp) + return -ERANGE; + *val = tmp; + return rv; +} +EXPORT_SYMBOL(_parse_integer_sc); -- 2.0.4 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/2] parse_integer: rewrite kstrto*() 2015-04-11 15:38 ` [PATCH 1/2] " Alexey Dobriyan @ 2015-04-11 15:43 ` Alexey Dobriyan 0 siblings, 0 replies; 6+ messages in thread From: Alexey Dobriyan @ 2015-04-11 15:43 UTC (permalink / raw) To: Andrew Morton; +Cc: linux-kernel, adobriyan Rewrite kstrto*() functions through parse_integer(). _kstrtoul() and _kstrtol() are removed because parse_integer() can dispatch based on BITS_PER_LONG saving function call. Also move function definitions and comment one instance. Remove redundant boilerplate comments from elsewhere. Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com> --- include/linux/kernel.h | 124 ----------------------- include/linux/parse-integer.h | 111 +++++++++++++++++++++ lib/kstrtox.c | 222 ------------------------------------------ lib/parse-integer.c | 38 ++++++- 4 files changed, 145 insertions(+), 350 deletions(-) --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -252,130 +252,6 @@ void do_exit(long error_code) void complete_and_exit(struct completion *, long) __noreturn; -/* Internal, do not use. */ -int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res); -int __must_check _kstrtol(const char *s, unsigned int base, long *res); - -int __must_check kstrtoull(const char *s, unsigned int base, unsigned long long *res); -int __must_check kstrtoll(const char *s, unsigned int base, long long *res); - -/** - * kstrtoul - convert a string to an unsigned long - * @s: The start of the string. The string must be null-terminated, and may also - * include a single newline before its terminating null. The first character - * may also be a plus sign, but not a minus sign. - * @base: The number base to use. The maximum supported base is 16. If base is - * given as 0, then the base of the string is automatically detected with the - * conventional semantics - If it begins with 0x the number will be parsed as a - * hexadecimal (case insensitive), if it otherwise begins with 0, it will be - * parsed as an octal number. Otherwise it will be parsed as a decimal. - * @res: Where to write the result of the conversion on success. - * - * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. - * Used as a replacement for the obsolete simple_strtoull. Return code must - * be checked. -*/ -static inline int __must_check kstrtoul(const char *s, unsigned int base, unsigned long *res) -{ - /* - * We want to shortcut function call, but - * __builtin_types_compatible_p(unsigned long, unsigned long long) = 0. - */ - if (sizeof(unsigned long) == sizeof(unsigned long long) && - __alignof__(unsigned long) == __alignof__(unsigned long long)) - return kstrtoull(s, base, (unsigned long long *)res); - else - return _kstrtoul(s, base, res); -} - -/** - * kstrtol - convert a string to a long - * @s: The start of the string. The string must be null-terminated, and may also - * include a single newline before its terminating null. The first character - * may also be a plus sign or a minus sign. - * @base: The number base to use. The maximum supported base is 16. If base is - * given as 0, then the base of the string is automatically detected with the - * conventional semantics - If it begins with 0x the number will be parsed as a - * hexadecimal (case insensitive), if it otherwise begins with 0, it will be - * parsed as an octal number. Otherwise it will be parsed as a decimal. - * @res: Where to write the result of the conversion on success. - * - * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. - * Used as a replacement for the obsolete simple_strtoull. Return code must - * be checked. - */ -static inline int __must_check kstrtol(const char *s, unsigned int base, long *res) -{ - /* - * We want to shortcut function call, but - * __builtin_types_compatible_p(long, long long) = 0. - */ - if (sizeof(long) == sizeof(long long) && - __alignof__(long) == __alignof__(long long)) - return kstrtoll(s, base, (long long *)res); - else - return _kstrtol(s, base, res); -} - -int __must_check kstrtouint(const char *s, unsigned int base, unsigned int *res); -int __must_check kstrtoint(const char *s, unsigned int base, int *res); - -static inline int __must_check kstrtou64(const char *s, unsigned int base, u64 *res) -{ - return kstrtoull(s, base, res); -} - -static inline int __must_check kstrtos64(const char *s, unsigned int base, s64 *res) -{ - return kstrtoll(s, base, res); -} - -static inline int __must_check kstrtou32(const char *s, unsigned int base, u32 *res) -{ - return kstrtouint(s, base, res); -} - -static inline int __must_check kstrtos32(const char *s, unsigned int base, s32 *res) -{ - return kstrtoint(s, base, res); -} - -int __must_check kstrtou16(const char *s, unsigned int base, u16 *res); -int __must_check kstrtos16(const char *s, unsigned int base, s16 *res); -int __must_check kstrtou8(const char *s, unsigned int base, u8 *res); -int __must_check kstrtos8(const char *s, unsigned int base, s8 *res); - -int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res); -int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res); -int __must_check kstrtoul_from_user(const char __user *s, size_t count, unsigned int base, unsigned long *res); -int __must_check kstrtol_from_user(const char __user *s, size_t count, unsigned int base, long *res); -int __must_check kstrtouint_from_user(const char __user *s, size_t count, unsigned int base, unsigned int *res); -int __must_check kstrtoint_from_user(const char __user *s, size_t count, unsigned int base, int *res); -int __must_check kstrtou16_from_user(const char __user *s, size_t count, unsigned int base, u16 *res); -int __must_check kstrtos16_from_user(const char __user *s, size_t count, unsigned int base, s16 *res); -int __must_check kstrtou8_from_user(const char __user *s, size_t count, unsigned int base, u8 *res); -int __must_check kstrtos8_from_user(const char __user *s, size_t count, unsigned int base, s8 *res); - -static inline int __must_check kstrtou64_from_user(const char __user *s, size_t count, unsigned int base, u64 *res) -{ - return kstrtoull_from_user(s, count, base, res); -} - -static inline int __must_check kstrtos64_from_user(const char __user *s, size_t count, unsigned int base, s64 *res) -{ - return kstrtoll_from_user(s, count, base, res); -} - -static inline int __must_check kstrtou32_from_user(const char __user *s, size_t count, unsigned int base, u32 *res) -{ - return kstrtouint_from_user(s, count, base, res); -} - -static inline int __must_check kstrtos32_from_user(const char __user *s, size_t count, unsigned int base, s32 *res) -{ - return kstrtoint_from_user(s, count, base, res); -} - /* Obsolete, do not use. Use parse_integer(), kstrto*() or kstrto*_from_user(). */ extern unsigned long simple_strtoul(const char *,char **,unsigned int); --- a/include/linux/parse-integer.h +++ b/include/linux/parse-integer.h @@ -75,5 +75,116 @@ int _parse_integer_ull(const char *s, unsigned int base, unsigned long long *val void _parse_integer_link_time_error(void); /* internal, do not use */ +#define PARSE_INTEGER_NEWLINE 0x80000000u + +/* + * Convert integer string representation terminated by \n\0 or \0 to an integer. + * + * Return 0 on success or -E. + * + * See parse_integer(). + */ +static inline int __must_check kstrtoull(const char *s, unsigned int base, unsigned long long *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtoll(const char *s, unsigned int base, long long *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtoul(const char *s, unsigned int base, unsigned long *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtol(const char *s, unsigned int base, long *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtouint(const char *s, unsigned int base, unsigned int *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtoint(const char *s, unsigned int base, int *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtou64(const char *s, unsigned int base, u64 *res) +{ + return kstrtoull(s, base, res); +} + +static inline int __must_check kstrtos64(const char *s, unsigned int base, s64 *res) +{ + return kstrtoll(s, base, res); +} + +static inline int __must_check kstrtou32(const char *s, unsigned int base, u32 *res) +{ + return kstrtouint(s, base, res); +} + +static inline int __must_check kstrtos32(const char *s, unsigned int base, s32 *res) +{ + return kstrtoint(s, base, res); +} + +static inline int __must_check kstrtou16(const char *s, unsigned int base, u16 *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtos16(const char *s, unsigned int base, s16 *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtou8(const char *s, unsigned int base, u8 *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +static inline int __must_check kstrtos8(const char *s, unsigned int base, s8 *res) +{ + return parse_integer(s, base | PARSE_INTEGER_NEWLINE, res); +} + +int __must_check kstrtoull_from_user(const char __user *s, size_t count, unsigned int base, unsigned long long *res); +int __must_check kstrtoll_from_user(const char __user *s, size_t count, unsigned int base, long long *res); +int __must_check kstrtoul_from_user(const char __user *s, size_t count, unsigned int base, unsigned long *res); +int __must_check kstrtol_from_user(const char __user *s, size_t count, unsigned int base, long *res); +int __must_check kstrtouint_from_user(const char __user *s, size_t count, unsigned int base, unsigned int *res); +int __must_check kstrtoint_from_user(const char __user *s, size_t count, unsigned int base, int *res); +int __must_check kstrtou16_from_user(const char __user *s, size_t count, unsigned int base, u16 *res); +int __must_check kstrtos16_from_user(const char __user *s, size_t count, unsigned int base, s16 *res); +int __must_check kstrtou8_from_user(const char __user *s, size_t count, unsigned int base, u8 *res); +int __must_check kstrtos8_from_user(const char __user *s, size_t count, unsigned int base, s8 *res); + +static inline int __must_check kstrtou64_from_user(const char __user *s, size_t count, unsigned int base, u64 *res) +{ + return kstrtoull_from_user(s, count, base, res); +} + +static inline int __must_check kstrtos64_from_user(const char __user *s, size_t count, unsigned int base, s64 *res) +{ + return kstrtoll_from_user(s, count, base, res); +} + +static inline int __must_check kstrtou32_from_user(const char __user *s, size_t count, unsigned int base, u32 *res) +{ + return kstrtouint_from_user(s, count, base, res); +} + +static inline int __must_check kstrtos32_from_user(const char __user *s, size_t count, unsigned int base, s32 *res) +{ + return kstrtoint_from_user(s, count, base, res); +} + +/* internal, do not use */ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base); #endif --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -67,228 +67,6 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long return rv; } -/** - * kstrtoull - convert a string to an unsigned long long - * @s: The start of the string. The string must be null-terminated, and may also - * include a single newline before its terminating null. The first character - * may also be a plus sign, but not a minus sign. - * @base: The number base to use. The maximum supported base is 16. If base is - * given as 0, then the base of the string is automatically detected with the - * conventional semantics - If it begins with 0x the number will be parsed as a - * hexadecimal (case insensitive), if it otherwise begins with 0, it will be - * parsed as an octal number. Otherwise it will be parsed as a decimal. - * @res: Where to write the result of the conversion on success. - * - * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. - * Used as a replacement for the obsolete simple_strtoull. Return code must - * be checked. - */ -int kstrtoull(const char *s, unsigned int base, unsigned long long *res) -{ - unsigned long long _res; - int rv; - - rv = parse_integer(s, base, &_res); - if (rv < 0) - return rv; - s += rv; - if (*s == '\n') - s++; - if (*s) - return -EINVAL; - *res = _res; - return 0; -} -EXPORT_SYMBOL(kstrtoull); - -/** - * kstrtoll - convert a string to a long long - * @s: The start of the string. The string must be null-terminated, and may also - * include a single newline before its terminating null. The first character - * may also be a plus sign or a minus sign. - * @base: The number base to use. The maximum supported base is 16. If base is - * given as 0, then the base of the string is automatically detected with the - * conventional semantics - If it begins with 0x the number will be parsed as a - * hexadecimal (case insensitive), if it otherwise begins with 0, it will be - * parsed as an octal number. Otherwise it will be parsed as a decimal. - * @res: Where to write the result of the conversion on success. - * - * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. - * Used as a replacement for the obsolete simple_strtoull. Return code must - * be checked. - */ -int kstrtoll(const char *s, unsigned int base, long long *res) -{ - long long _res; - int rv; - - rv = parse_integer(s, base, &_res); - if (rv < 0) - return rv; - s += rv; - if (*s == '\n') - s++; - if (*s) - return -EINVAL; - *res = _res; - return 0; -} -EXPORT_SYMBOL(kstrtoll); - -/* Internal, do not use. */ -int _kstrtoul(const char *s, unsigned int base, unsigned long *res) -{ - unsigned long long tmp; - int rv; - - rv = kstrtoull(s, base, &tmp); - if (rv < 0) - return rv; - if (tmp != (unsigned long long)(unsigned long)tmp) - return -ERANGE; - *res = tmp; - return 0; -} -EXPORT_SYMBOL(_kstrtoul); - -/* Internal, do not use. */ -int _kstrtol(const char *s, unsigned int base, long *res) -{ - long long tmp; - int rv; - - rv = kstrtoll(s, base, &tmp); - if (rv < 0) - return rv; - if (tmp != (long long)(long)tmp) - return -ERANGE; - *res = tmp; - return 0; -} -EXPORT_SYMBOL(_kstrtol); - -/** - * kstrtouint - convert a string to an unsigned int - * @s: The start of the string. The string must be null-terminated, and may also - * include a single newline before its terminating null. The first character - * may also be a plus sign, but not a minus sign. - * @base: The number base to use. The maximum supported base is 16. If base is - * given as 0, then the base of the string is automatically detected with the - * conventional semantics - If it begins with 0x the number will be parsed as a - * hexadecimal (case insensitive), if it otherwise begins with 0, it will be - * parsed as an octal number. Otherwise it will be parsed as a decimal. - * @res: Where to write the result of the conversion on success. - * - * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. - * Used as a replacement for the obsolete simple_strtoull. Return code must - * be checked. - */ -int kstrtouint(const char *s, unsigned int base, unsigned int *res) -{ - unsigned long long tmp; - int rv; - - rv = kstrtoull(s, base, &tmp); - if (rv < 0) - return rv; - if (tmp != (unsigned long long)(unsigned int)tmp) - return -ERANGE; - *res = tmp; - return 0; -} -EXPORT_SYMBOL(kstrtouint); - -/** - * kstrtoint - convert a string to an int - * @s: The start of the string. The string must be null-terminated, and may also - * include a single newline before its terminating null. The first character - * may also be a plus sign or a minus sign. - * @base: The number base to use. The maximum supported base is 16. If base is - * given as 0, then the base of the string is automatically detected with the - * conventional semantics - If it begins with 0x the number will be parsed as a - * hexadecimal (case insensitive), if it otherwise begins with 0, it will be - * parsed as an octal number. Otherwise it will be parsed as a decimal. - * @res: Where to write the result of the conversion on success. - * - * Returns 0 on success, -ERANGE on overflow and -EINVAL on parsing error. - * Used as a replacement for the obsolete simple_strtoull. Return code must - * be checked. - */ -int kstrtoint(const char *s, unsigned int base, int *res) -{ - long long tmp; - int rv; - - rv = kstrtoll(s, base, &tmp); - if (rv < 0) - return rv; - if (tmp != (long long)(int)tmp) - return -ERANGE; - *res = tmp; - return 0; -} -EXPORT_SYMBOL(kstrtoint); - -int kstrtou16(const char *s, unsigned int base, u16 *res) -{ - unsigned long long tmp; - int rv; - - rv = kstrtoull(s, base, &tmp); - if (rv < 0) - return rv; - if (tmp != (unsigned long long)(u16)tmp) - return -ERANGE; - *res = tmp; - return 0; -} -EXPORT_SYMBOL(kstrtou16); - -int kstrtos16(const char *s, unsigned int base, s16 *res) -{ - long long tmp; - int rv; - - rv = kstrtoll(s, base, &tmp); - if (rv < 0) - return rv; - if (tmp != (long long)(s16)tmp) - return -ERANGE; - *res = tmp; - return 0; -} -EXPORT_SYMBOL(kstrtos16); - -int kstrtou8(const char *s, unsigned int base, u8 *res) -{ - unsigned long long tmp; - int rv; - - rv = kstrtoull(s, base, &tmp); - if (rv < 0) - return rv; - if (tmp != (unsigned long long)(u8)tmp) - return -ERANGE; - *res = tmp; - return 0; -} -EXPORT_SYMBOL(kstrtou8); - -int kstrtos8(const char *s, unsigned int base, s8 *res) -{ - long long tmp; - int rv; - - rv = kstrtoll(s, base, &tmp); - if (rv < 0) - return rv; - if (tmp != (long long)(s8)tmp) - return -ERANGE; - *res = tmp; - return 0; -} -EXPORT_SYMBOL(kstrtos8); - #define kstrto_from_user(f, g, type) \ int f(const char __user *s, size_t count, unsigned int base, type *res) \ { \ --- a/lib/parse-integer.c +++ b/lib/parse-integer.c @@ -31,7 +31,7 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) return s; } -static int __parse_integer(const char *s, unsigned int base, unsigned long long *val) +static int ___parse_integer(const char *s, unsigned int base, unsigned long long *val) { const char *s0 = s, *sd; unsigned long long acc; @@ -63,6 +63,27 @@ static int __parse_integer(const char *s, unsigned int base, unsigned long long return s - s0; } +static int __parse_integer(const char *s, unsigned int base, unsigned long long *val) +{ + if (base & PARSE_INTEGER_NEWLINE) { + unsigned long long _val; + int rv; + + /* Accept "integer\0" or "integer\n\0" */ + rv = ___parse_integer(s, base & ~PARSE_INTEGER_NEWLINE, &_val); + if (rv < 0) + return rv; + s += rv; + if (*s == '\n') + s++; + if (*s) + return -EINVAL; + *val = _val; + return 0; + } else + return ___parse_integer(s, base, val); +} + int _parse_integer_ull(const char *s, unsigned int base, unsigned long long *val) { int rv; @@ -73,7 +94,10 @@ int _parse_integer_ull(const char *s, unsigned int base, unsigned long long *val rv = __parse_integer(s + 1, base, val); if (rv < 0) return rv; - return rv + 1; + if (base & PARSE_INTEGER_NEWLINE) + return 0; + else + return rv + 1; } else return __parse_integer(s, base, val); } @@ -91,7 +115,10 @@ int _parse_integer_ll(const char *s, unsigned int base, long long *val) if ((long long)-tmp >= 0) return -ERANGE; *val = -tmp; - return rv + 1; + if (base & PARSE_INTEGER_NEWLINE) + return 0; + else + return rv + 1; } else if (*s == '+') { rv = __parse_integer(s + 1, base, &tmp); if (rv < 0) @@ -99,7 +126,10 @@ int _parse_integer_ll(const char *s, unsigned int base, long long *val) if ((long long)tmp < 0) return -ERANGE; *val = tmp; - return rv + 1; + if (base & PARSE_INTEGER_NEWLINE) + return 0; + else + return rv + 1; } else { rv = __parse_integer(s, base, &tmp); if (rv < 0) -- 2.0.4 ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2015-04-11 15:43 UTC | newest] Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2015-04-09 15:26 [PATCH] Add parse_integer() (replacement for simple_strto*()) Alexey Dobriyan 2015-04-09 15:28 ` Alexey Dobriyan 2015-04-09 20:02 ` Andrew Morton 2015-04-11 15:36 ` Alexey Dobriyan 2015-04-11 15:38 ` [PATCH 1/2] " Alexey Dobriyan 2015-04-11 15:43 ` [PATCH 2/2] parse_integer: rewrite kstrto*() Alexey Dobriyan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®