lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <155223699475.4075.10085989782784489732.stgit@buzz>
Date:   Sun, 10 Mar 2019 19:56:34 +0300
From:   Konstantin Khlebnikov <khlebnikov@...dex-team.ru>
To:     linux-kernel@...r.kernel.org
Cc:     Tejun Heo <tj@...nel.org>,
        Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
        Andrew Morton <akpm@...ux-foundation.org>,
        Linus Torvalds <torvalds@...ux-foundation.org>,
        Alexey Dobriyan <adobriyan@...il.com>
Subject: [PATCH v1 3/6] lib: scanf: add vsscanf feature for matching end of
 text

Traditional sscanf ignores trailing unmatched characters,
because it derives from design of streaming fscanf().

Without extra care this leads to confusing parsing results:
"%d" successfully parses "0x1" as 0 and "1M" as 1.

Silent ignore of trailing input also could hide bugs and
unexpectedly break backward compatibility at any change.

This patch adds end of text matching into sscanf format by
ASCII End-Of-Transfer character '\004' (EOT aka ^D).

EOT stops matching and if input text haven't ended and adds
flag SCANF_MORE to the result value. Result stays positive.

EOT allows to rephrase common end of text checks like:

int end;
if (sscanf(buf, fmt "%n", ..., &end) == NR && !buf[end])

char dummy;
if (sscanf(buf, fmt "%c", ..., &dummy) == NR - 1)

purely in format string without extra code or arguments:

if (sscanf(buf, fmt KERN_EOT, ...) == NR)

This checks that text ends strictly after previous matched character.
For skipping trailing white spaces simply add space before EOT.

Signed-off-by: Konstantin Khlebnikov <khlebnikov@...dex-team.ru>
---
 include/linux/kernel.h |    6 ++++++
 lib/vsprintf.c         |   11 ++++++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 34a5036debd3..32726b25eaa5 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -476,6 +476,12 @@ char *kvasprintf(gfp_t gfp, const char *fmt, va_list args);
 extern __printf(2, 0)
 const char *kvasprintf_const(gfp_t gfp, const char *fmt, va_list args);
 
+#define KERN_EOT	"\004"	/* ASCII End Of Transfer */
+#define KERN_EOT_ASCII	'\004'
+
+/* sscanf adds this to the result if EOT does not match end of text */
+#define SCANF_MORE	(1 << 30)
+
 extern __scanf(2, 3)
 int sscanf(const char *, const char *, ...);
 extern __scanf(2, 0)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index ada0501f1525..66debf42387a 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -3005,7 +3005,8 @@ EXPORT_SYMBOL_GPL(bprintf);
  * @fmt:	format of buffer
  * @args:	arguments
  *
- * Returns number of successfully matched arguments.
+ * Returns number of successfully matched arguments,
+ * plus SCANF_MORE bit flag if EOT does not matched end of text.
  *
  * This function implements only basic vsscanf features:
  *
@@ -3027,6 +3028,8 @@ EXPORT_SYMBOL_GPL(bprintf);
  * - %s without field width limited with SHRT_MAX
  * - "%*..." simply skips non white-space characters without conversion
  * - integer overflows are handled as matching failure
+ * - KERN_EOT ("\004") matches end of string otherwise stops parsing and
+ *   returns count matched arguments plus SCANF_MORE bit flag,
  */
 int vsscanf(const char *buf, const char *fmt, va_list args)
 {
@@ -3055,6 +3058,12 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
 
 		/* anything that is not a conversion must match exactly */
 		if (*fmt != '%' && *fmt) {
+			/* EOT in format string: text must end here */
+			if (*fmt == KERN_EOT_ASCII) {
+				if (*str)
+					num |= SCANF_MORE;
+				break;
+			}
 			if (*fmt++ != *str++)
 				break;
 			continue;

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ