Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33ee3b2e authored by Alexey Dobriyan's avatar Alexey Dobriyan Committed by Linus Torvalds
Browse files

kstrto*: converting strings to integers done (hopefully) right



1. simple_strto*() do not contain overflow checks and crufty,
   libc way to indicate failure.
2. strict_strto*() also do not have overflow checks but the name and
   comments pretend they do.
3. Both families have only "long long" and "long" variants,
   but users want strtou8()
4. Both "simple" and "strict" prefixes are wrong:
   Simple doesn't exactly say what's so simple, strict should not exist
   because conversion should be strict by default.

The solution is to use "k" prefix and add convertors for more types.
Enter
	kstrtoull()
	kstrtoll()
	kstrtoul()
	kstrtol()
	kstrtouint()
	kstrtoint()

	kstrtou64()
	kstrtos64()
	kstrtou32()
	kstrtos32()
	kstrtou16()
	kstrtos16()
	kstrtou8()
	kstrtos8()

Include runtime testsuite (somewhat incomplete) as well.

strict_strto*() become deprecated, stubbed to kstrto*() and
eventually will be removed altogether.

Use kstrto*() in code today!

Note: on some archs _kstrtoul() and _kstrtol() are left in tree, even if
      they'll be unused at runtime. This is temporarily solution,
      because I don't want to hardcode list of archs where these
      functions aren't needed. Current solution with sizeof() and
      __alignof__ at least always works.

Signed-off-by: default avatarAlexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 8a5700cd
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -94,9 +94,6 @@ extern int viafb_LCD_ON;
extern int viafb_DVI_ON;
extern int viafb_hotplug;

extern int strict_strtoul(const char *cp, unsigned int base,
	unsigned long *res);

u8 viafb_gpio_i2c_read_lvds(struct lvds_setting_information
	*plvds_setting_info, struct lvds_chip_information
	*plvds_chip_info, u8 index);
+66 −4
Original line number Diff line number Diff line
@@ -187,14 +187,76 @@ NORET_TYPE void do_exit(long error_code)
	ATTRIB_NORET;
NORET_TYPE void complete_and_exit(struct completion *, long)
	ATTRIB_NORET;

/* 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);
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);
}

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

extern unsigned long simple_strtoul(const char *,char **,unsigned int);
extern long simple_strtol(const char *,char **,unsigned int);
extern unsigned long long simple_strtoull(const char *,char **,unsigned int);
extern long long simple_strtoll(const char *,char **,unsigned int);
extern int __must_check strict_strtoul(const char *, unsigned int, unsigned long *);
extern int __must_check strict_strtol(const char *, unsigned int, long *);
extern int __must_check strict_strtoull(const char *, unsigned int, unsigned long long *);
extern int __must_check strict_strtoll(const char *, unsigned int, long long *);
#define strict_strtoul	kstrtoul
#define strict_strtol	kstrtol
#define strict_strtoull	kstrtoull
#define strict_strtoll	kstrtoll

extern int sprintf(char * buf, const char * fmt, ...)
	__attribute__ ((format (printf, 2, 3)));
extern int vsprintf(char *buf, const char *, va_list)
+3 −0
Original line number Diff line number Diff line
@@ -1250,3 +1250,6 @@ source "samples/Kconfig"
source "lib/Kconfig.kgdb"

source "lib/Kconfig.kmemcheck"

config TEST_KSTRTOX
	tristate "Test kstrto*() family of functions at runtime"
+2 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ lib-y += kobject.o kref.o klist.o
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
	 bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
	 string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o
obj-y += kstrtox.o
obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o

ifeq ($(CONFIG_DEBUG_KOBJECT),y)
CFLAGS_kobject.o += -DDEBUG

lib/kstrtox.c

0 → 100644
+227 −0
Original line number Diff line number Diff line
/*
 * Convert integer string representation to an integer.
 * If an integer doesn't fit into specified type, -E is returned.
 *
 * Integer starts with optional sign.
 * kstrtou*() functions do not 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.
 */
#include <linux/ctype.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/types.h>

static inline char _tolower(const char c)
{
	return c | 0x20;
}

static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
	unsigned long long acc;
	int ok;

	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;

	acc = 0;
	ok = 0;
	while (*s) {
		unsigned int val;

		if ('0' <= *s && *s <= '9')
			val = *s - '0';
		else if ('a' <= _tolower(*s) && _tolower(*s) <= 'f')
			val = _tolower(*s) - 'a' + 10;
		else if (*s == '\n') {
			if (*(s + 1) == '\0')
				break;
			else
				return -EINVAL;
		} else
			return -EINVAL;

		if (val >= base)
			return -EINVAL;
		if (acc > div_u64(ULLONG_MAX - val, base))
			return -ERANGE;
		acc = acc * base + val;
		ok = 1;

		s++;
	}
	if (!ok)
		return -EINVAL;
	*res = acc;
	return 0;
}

int kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
	if (s[0] == '+')
		s++;
	return _kstrtoull(s, base, res);
}
EXPORT_SYMBOL(kstrtoull);

int kstrtoll(const char *s, unsigned int base, long long *res)
{
	unsigned long long tmp;
	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;
	}
	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);

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

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