lkml.org 
[lkml]   [2015]   [May]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 2/2] vsprintf: Add support for userspace strings
    Date
    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@nod.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


    \
     
     \ /
      Last update: 2015-05-10 22:21    [W:2.119 / U:0.136 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site