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

Commit 53809751 authored by Jan Beulich's avatar Jan Beulich Committed by Linus Torvalds
Browse files

sscanf: don't ignore field widths for numeric conversions



This is another step towards better standard conformance.  Rather than
adding a local buffer to store the specified portion of the string (with
the need to enforce an arbitrary maximum supported width to limit the
buffer size), do a maximum width conversion and then drop as much of it as
is necessary to meet the caller's request.

Also fail on negative field widths.

Uses the deprecated simple_strto*() functions because kstrtoXX() fail on
non-zero terminated strings.

Signed-off-by: default avatarJan Beulich <jbeulich@suse.com>
Cc: Alexey 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 375da3a7
Loading
Loading
Loading
Loading
+53 −43
Original line number Original line Diff line number Diff line
@@ -23,12 +23,12 @@
#include <linux/ctype.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/kallsyms.h>
#include <linux/kallsyms.h>
#include <linux/math64.h>
#include <linux/uaccess.h>
#include <linux/uaccess.h>
#include <linux/ioport.h>
#include <linux/ioport.h>
#include <net/addrconf.h>
#include <net/addrconf.h>


#include <asm/page.h>		/* for PAGE_SIZE */
#include <asm/page.h>		/* for PAGE_SIZE */
#include <asm/div64.h>
#include <asm/sections.h>	/* for dereference_function_descriptor() */
#include <asm/sections.h>	/* for dereference_function_descriptor() */


#include "kstrtox.h"
#include "kstrtox.h"
@@ -2016,7 +2016,11 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
	char digit;
	char digit;
	int num = 0;
	int num = 0;
	u8 qualifier;
	u8 qualifier;
	u8 base;
	unsigned int base;
	union {
		long long s;
		unsigned long long u;
	} val;
	s16 field_width;
	s16 field_width;
	bool is_sign;
	bool is_sign;


@@ -2056,8 +2060,11 @@ int vsscanf(const char *buf, const char *fmt, va_list args)


		/* get field width */
		/* get field width */
		field_width = -1;
		field_width = -1;
		if (isdigit(*fmt))
		if (isdigit(*fmt)) {
			field_width = skip_atoi(&fmt);
			field_width = skip_atoi(&fmt);
			if (field_width <= 0)
				break;
		}


		/* get conversion qualifier */
		/* get conversion qualifier */
		qualifier = -1;
		qualifier = -1;
@@ -2157,58 +2164,61 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
		    || (base == 0 && !isdigit(digit)))
		    || (base == 0 && !isdigit(digit)))
			break;
			break;


		if (is_sign)
			val.s = qualifier != 'L' ?
				simple_strtol(str, &next, base) :
				simple_strtoll(str, &next, base);
		else
			val.u = qualifier != 'L' ?
				simple_strtoul(str, &next, base) :
				simple_strtoull(str, &next, base);

		if (field_width > 0 && next - str > field_width) {
			if (base == 0)
				_parse_integer_fixup_radix(str, &base);
			while (next - str > field_width) {
				if (is_sign)
					val.s = div_s64(val.s, base);
				else
					val.u = div_u64(val.u, base);
				--next;
			}
		}

		switch (qualifier) {
		switch (qualifier) {
		case 'H':	/* that's 'hh' in format */
		case 'H':	/* that's 'hh' in format */
			if (is_sign) {
			if (is_sign)
				signed char *s = (signed char *)va_arg(args, signed char *);
				*va_arg(args, signed char *) = val.s;
				*s = (signed char)simple_strtol(str, &next, base);
			else
			} else {
				*va_arg(args, unsigned char *) = val.u;
				unsigned char *s = (unsigned char *)va_arg(args, unsigned char *);
				*s = (unsigned char)simple_strtoul(str, &next, base);
			}
			break;
			break;
		case 'h':
		case 'h':
			if (is_sign) {
			if (is_sign)
				short *s = (short *)va_arg(args, short *);
				*va_arg(args, short *) = val.s;
				*s = (short)simple_strtol(str, &next, base);
			else
			} else {
				*va_arg(args, unsigned short *) = val.u;
				unsigned short *s = (unsigned short *)va_arg(args, unsigned short *);
				*s = (unsigned short)simple_strtoul(str, &next, base);
			}
			break;
			break;
		case 'l':
		case 'l':
			if (is_sign) {
			if (is_sign)
				long *l = (long *)va_arg(args, long *);
				*va_arg(args, long *) = val.s;
				*l = simple_strtol(str, &next, base);
			else
			} else {
				*va_arg(args, unsigned long *) = val.u;
				unsigned long *l = (unsigned long *)va_arg(args, unsigned long *);
				*l = simple_strtoul(str, &next, base);
			}
			break;
			break;
		case 'L':
		case 'L':
			if (is_sign) {
			if (is_sign)
				long long *l = (long long *)va_arg(args, long long *);
				*va_arg(args, long long *) = val.s;
				*l = simple_strtoll(str, &next, base);
			else
			} else {
				*va_arg(args, unsigned long long *) = val.u;
				unsigned long long *l = (unsigned long long *)va_arg(args, unsigned long long *);
				*l = simple_strtoull(str, &next, base);
			}
			break;
			break;
		case 'Z':
		case 'Z':
		case 'z':
		case 'z':
		{
			*va_arg(args, size_t *) = val.u;
			size_t *s = (size_t *)va_arg(args, size_t *);
			*s = (size_t)simple_strtoul(str, &next, base);
		}
			break;
			break;
		default:
		default:
			if (is_sign) {
			if (is_sign)
				int *i = (int *)va_arg(args, int *);
				*va_arg(args, int *) = val.s;
				*i = (int)simple_strtol(str, &next, base);
			else
			} else {
				*va_arg(args, unsigned int *) = val.u;
				unsigned int *i = (unsigned int *)va_arg(args, unsigned int*);
				*i = (unsigned int)simple_strtoul(str, &next, base);
			}
			break;
			break;
		}
		}
		num++;
		num++;