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: <1431286936-4333-3-git-send-email-richard@nod.at>
Date:	Sun, 10 May 2015 21:42:16 +0200
From:	Richard Weinberger <richard@....at>
To:	akpm@...ux-foundation.org
Cc:	linux-kernel@...r.kernel.org, Richard Weinberger <richard@....at>
Subject: [PATCH 2/2] vsprintf: Add support for userspace strings

Add %pL format string to print userspace strings.
It works like %s but does copy_from_user() instead
of a memcpy().

Signed-off-by: Richard Weinberger <richard@....at>
---
 Documentation/printk-formats.txt |  6 +++
 lib/vsprintf.c                   | 89 ++++++++++++++++++++++++++++++++++------
 2 files changed, 82 insertions(+), 13 deletions(-)

diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt
index 2216eb1..74f6038 100644
--- a/Documentation/printk-formats.txt
+++ b/Documentation/printk-formats.txt
@@ -284,6 +284,12 @@ bitmap and its derivatives such as cpumask and nodemask:
 
 	Passed by reference.
 
+userspace string:
+
+	%pL
+
+	Like %s but works on strings located in userspace.
+
 Thank you for your cooperation and attention.
 
 
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 092d5a7..4138340 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -355,6 +355,7 @@ int num_to_str(char *buf, int size, unsigned long long num)
 #define ZEROPAD	16		/* pad with zero, must be 16 == '0' - ' ' */
 #define SMALL	32		/* use lowercase in hex (must be 32 == 0x20) */
 #define SPECIAL	64		/* prefix hex with "0x", octal with "0" */
+#define USER	128		/* value points to user memory */
 
 enum format_type {
 	FORMAT_TYPE_NONE, /* Just a string part */
@@ -510,12 +511,27 @@ static noinline_for_stack
 char *string(char *buf, char *end, const char *s, struct printf_spec spec)
 {
 	int len, i;
+	int is_user = spec.flags & USER;
+
+	if (is_user) {
+		int max_len = min_t(unsigned long, spec.precision, PAGE_SIZE);
+
+		len = strnlen_user(s, max_len);
+		if (len == 0) {
+			s = "(bad address)";
+			len = strnlen(s, spec.precision);
+			is_user = 0;
+		} else {
+			/* strnlen_user() counts also the NUL byte */
+			len--;
+		}
+	} else {
+		if ((unsigned long)s > (unsigned long)-PAGE_SIZE ||
+		    (unsigned long)s < PAGE_SIZE)
+			s = "(null)";
 
-	if ((unsigned long)s > (unsigned long)-PAGE_SIZE ||
-	    (unsigned long)s < PAGE_SIZE)
-		s = "(null)";
-
-	len = strnlen(s, spec.precision);
+		len = strnlen(s, spec.precision);
+	}
 
 	if (!(spec.flags & LEFT)) {
 		while (len < spec.field_width--) {
@@ -525,8 +541,17 @@ char *string(char *buf, char *end, const char *s, struct printf_spec spec)
 		}
 	}
 	for (i = 0; i < len; ++i) {
-		if (buf < end)
+		if (buf >= end)
+			goto next;
+
+		if (is_user) {
+			if (get_user(*buf, s)) {
+				/* get_user() failed, add a blank */
+				*buf = ' ';
+			}
+		} else
 			*buf = *s;
+next:
 		++buf; ++s;
 	}
 	while (len < spec.field_width--) {
@@ -1743,7 +1768,19 @@ qualifier:
 
 	case 'p':
 		spec->type = FORMAT_TYPE_PTR;
-		return ++fmt - start;
+		fmt++;
+		/*
+		 * handle %pL here and turn it into a special FORMAT_TYPE_STR
+		 * type.
+		 * %pL is much like the %s case. This way we keep vbin_printf()
+		 * straight forward.
+		 */
+		if (*fmt == 'L') {
+			spec->type = FORMAT_TYPE_STR;
+			spec->flags |= USER;
+			fmt++;
+		}
+		return fmt - start;
 
 	case '%':
 		spec->type = FORMAT_TYPE_PERCENT_CHAR;
@@ -2204,14 +2241,33 @@ do {									\
 
 		case FORMAT_TYPE_STR: {
 			const char *save_str = va_arg(args, char *);
+			int is_user = spec.flags & USER;
 			size_t len;
 
-			if ((unsigned long)save_str > (unsigned long)-PAGE_SIZE
-					|| (unsigned long)save_str < PAGE_SIZE)
-				save_str = "(null)";
-			len = strlen(save_str) + 1;
-			if (str + len < end)
-				memcpy(str, save_str, len);
+			if (is_user) {
+				len = strnlen_user(save_str, PAGE_SIZE);
+				if (len == 0) {
+					save_str = "(bad address)";
+					is_user = 0;
+				} else {
+					if (copy_from_user(str, save_str, len)) {
+						save_str = "(bad address)";
+						is_user = 0;
+					}
+				}
+			}
+
+			if (!is_user) {
+				if ((unsigned long)save_str > (unsigned long)-PAGE_SIZE
+						|| (unsigned long)save_str < PAGE_SIZE)
+					save_str = "(null)";
+
+				len = strlen(save_str) + 1;
+
+				if (str + len < end)
+					memcpy(str, save_str, len);
+			}
+
 			str += len;
 			break;
 		}
@@ -2364,6 +2420,13 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
 		case FORMAT_TYPE_STR: {
 			const char *str_arg = args;
 			args += strlen(str_arg) + 1;
+
+			/*
+			 * vbin_printf() sanitized the user provided string
+			 * for us and copied it into our binary buffer.
+			 */
+			spec.flags &= ~USER;
+
 			str = string(str, end, (char *)str_arg, spec);
 			break;
 		}
-- 
1.8.4.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ