Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752069AbbEJTmx (ORCPT ); Sun, 10 May 2015 15:42:53 -0400 Received: from mail.sigma-star.at ([95.130.255.111]:14387 "EHLO mail.sigma-star.at" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751870AbbEJTm1 (ORCPT ); Sun, 10 May 2015 15:42:27 -0400 From: Richard Weinberger To: akpm@linux-foundation.org Cc: linux-kernel@vger.kernel.org, Richard Weinberger Subject: [PATCH 2/2] vsprintf: Add support for userspace strings Date: Sun, 10 May 2015 21:42:16 +0200 Message-Id: <1431286936-4333-3-git-send-email-richard@nod.at> X-Mailer: git-send-email 1.8.4.5 In-Reply-To: <1431286936-4333-1-git-send-email-richard@nod.at> References: <1431286936-4333-1-git-send-email-richard@nod.at> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4641 Lines: 174 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 --- 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/