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

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
...@@ -23,12 +23,12 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 *); break;
*s = (size_t)simple_strtoul(str, &next, base);
}
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++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment