From: Fredrik Noring <noring@nocrew.org>
To: Jan Beulich <jbeulich@suse.com>,
Andrew Morton <akpm@linux-foundation.org>,
Alexey Dobriyan <adobriyan@gmail.com>,
Jessica Yu <jeyu@redhat.com>
Cc: linux-kernel@vger.kernel.org
Subject: [PATCH RFC] sscanf: Fix integer overflow with sscanf field width
Date: Thu, 23 May 2019 19:27:58 +0200 [thread overview]
Message-ID: <20190523172758.GA54373@sx9> (raw)
Hi,
This fixes 53809751ac23 ("sscanf: don't ignore field widths for numeric
conversions"). sscanf overflows integers with simple strings such as dates.
As an example, consider
r = sscanf("20190523123456", "%4d%2d%2d%2d%2d%2d",
&year, &month, &day,
&hour, &minute, &second);
pr_info("%d %04d-%02d-%2d %02d:%02d:%02d\n",
r,
year, month, day,
hour, minute, second);
On a 32-bit machine this prints
6 0000-05-23 12:34:56
where the year is zero, and not 2019 as expected. The reason is that sscanf
attempts to read 20190523123456 as a whole integer and then divide it with
10^10 to obtain 2019, which obviously fails. Of course, 64-bit machines fail
similarly on longer numerical strings.
I'm offering a simple patch to correct this below. The idea is to have a
variant of _parse_integer() called _parse_integer_end(), with the ability
to stop consuming digits. The functions
simple_{strtol,strtoll,strtoul,strtoull}()
now have the corresponding
sscanf_{strtol,strtoll,strtoul,strtoull}()
taking a field width into account. There are some code duplication issues
etc. so one might consider making more extensive changes than these.
What are your thoughts?
Fredrik
diff --git a/lib/kstrtox.c b/lib/kstrtox.c
--- a/lib/kstrtox.c
+++ b/lib/kstrtox.c
@@ -45,14 +45,15 @@ const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
*
* Don't you dare use this function.
*/
-unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p)
+unsigned int _parse_integer_end(const char *s, const char *e,
+ unsigned int base, unsigned long long *p)
{
unsigned long long res;
unsigned int rv;
res = 0;
rv = 0;
- while (1) {
+ while (!e || s < e) {
unsigned int c = *s;
unsigned int lc = c | 0x20; /* don't tolower() this line */
unsigned int val;
@@ -82,6 +83,11 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long
return rv;
}
+unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *p)
+{
+ return _parse_integer_end(s, NULL, base, p);
+}
+
static int _kstrtoull(const char *s, unsigned int base, unsigned long long *res)
{
unsigned long long _res;
diff --git a/lib/kstrtox.h b/lib/kstrtox.h
--- a/lib/kstrtox.h
+++ b/lib/kstrtox.h
@@ -4,6 +4,8 @@
#define KSTRTOX_OVERFLOW (1U << 31)
const char *_parse_integer_fixup_radix(const char *s, unsigned int *base);
+unsigned int _parse_integer_end(const char *s, const char *e,
+ unsigned int base, unsigned long long *p);
unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res);
#endif
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -123,6 +123,48 @@ long long simple_strtoll(const char *cp, char **endp, unsigned int base)
}
EXPORT_SYMBOL(simple_strtoll);
+static unsigned long long sscanf_strtoull(const char *cp, int field_width,
+ char **endp, unsigned int base)
+{
+ const char *e = field_width > 0 ? &cp[field_width] : NULL;
+ unsigned long long result;
+ unsigned int rv;
+
+ cp = _parse_integer_fixup_radix(cp, &base);
+ rv = _parse_integer_end(cp, e, base, &result);
+ /* FIXME */
+ cp += (rv & ~KSTRTOX_OVERFLOW);
+
+ if (endp)
+ *endp = (char *)cp;
+
+ return result;
+}
+
+static unsigned long sscanf_strtoul(const char *cp, int field_width,
+ char **endp, unsigned int base)
+{
+ return sscanf_strtoull(cp, field_width, endp, base);
+}
+
+static long sscanf_strtol(const char *cp, int field_width,
+ char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ return -sscanf_strtoul(cp + 1, field_width - 1, endp, base);
+
+ return sscanf_strtoul(cp, field_width, endp, base);
+}
+
+static long long sscanf_strtoll(const char *cp, int field_width,
+ char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ return -sscanf_strtoull(cp + 1, field_width - 1, endp, base);
+
+ return sscanf_strtoull(cp, field_width, endp, base);
+}
+
static noinline_for_stack
int skip_atoi(const char **s)
{
@@ -3330,24 +3372,12 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
if (is_sign)
val.s = qualifier != 'L' ?
- simple_strtol(str, &next, base) :
- simple_strtoll(str, &next, base);
+ sscanf_strtol(str, field_width, &next, base) :
+ sscanf_strtoll(str, field_width, &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;
- }
- }
+ sscanf_strtoul(str, field_width, &next, base) :
+ sscanf_strtoull(str, field_width, &next, base);
switch (qualifier) {
case 'H': /* that's 'hh' in format */
next reply other threads:[~2019-05-23 17:28 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-05-23 17:27 Fredrik Noring [this message]
2019-05-24 7:24 ` [PATCH RFC] sscanf: Fix integer overflow with sscanf field width Jan Beulich
-- strict thread matches above, loose matches on Subject: below --
2019-05-23 18:05 Alexey Dobriyan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190523172758.GA54373@sx9 \
--to=noring@nocrew.org \
--cc=adobriyan@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=jbeulich@suse.com \
--cc=jeyu@redhat.com \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.