Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lib/string_helpers: introduce generic string_unescape

There are several places in kernel where modules unescapes input to convert
C-Style Escape Sequences into byte codes.

The patch provides generic implementation of such approach. Test cases are
also included into the patch.

[akpm@linux-foundation.org: clarify comment]
[akpm@linux-foundation.org: export get_random_int() to modules]
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Samuel Thibault <samuel.thibault@ens-lyon.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: William Hubbs <w.d.hubbs@gmail.com>
Cc: Chris Brannon <chris@the-brannons.com>
Cc: Kirk Reiser <kirk@braille.uwo.ca>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Andy Shevchenko and committed by
Linus Torvalds
16c7fa05 e1d12f32

+301 -1
+1
drivers/char/random.c
··· 1485 1485 1486 1486 return ret; 1487 1487 } 1488 + EXPORT_SYMBOL(get_random_int); 1488 1489 1489 1490 /* 1490 1491 * randomize_range() returns a start address such that
+58
include/linux/string_helpers.h
··· 13 13 int string_get_size(u64 size, enum string_size_units units, 14 14 char *buf, int len); 15 15 16 + #define UNESCAPE_SPACE 0x01 17 + #define UNESCAPE_OCTAL 0x02 18 + #define UNESCAPE_HEX 0x04 19 + #define UNESCAPE_SPECIAL 0x08 20 + #define UNESCAPE_ANY \ 21 + (UNESCAPE_SPACE | UNESCAPE_OCTAL | UNESCAPE_HEX | UNESCAPE_SPECIAL) 22 + 23 + /** 24 + * string_unescape - unquote characters in the given string 25 + * @src: source buffer (escaped) 26 + * @dst: destination buffer (unescaped) 27 + * @size: size of the destination buffer (0 to unlimit) 28 + * @flags: combination of the flags (bitwise OR): 29 + * %UNESCAPE_SPACE: 30 + * '\f' - form feed 31 + * '\n' - new line 32 + * '\r' - carriage return 33 + * '\t' - horizontal tab 34 + * '\v' - vertical tab 35 + * %UNESCAPE_OCTAL: 36 + * '\NNN' - byte with octal value NNN (1 to 3 digits) 37 + * %UNESCAPE_HEX: 38 + * '\xHH' - byte with hexadecimal value HH (1 to 2 digits) 39 + * %UNESCAPE_SPECIAL: 40 + * '\"' - double quote 41 + * '\\' - backslash 42 + * '\a' - alert (BEL) 43 + * '\e' - escape 44 + * %UNESCAPE_ANY: 45 + * all previous together 46 + * 47 + * Returns amount of characters processed to the destination buffer excluding 48 + * trailing '\0'. 49 + * 50 + * Because the size of the output will be the same as or less than the size of 51 + * the input, the transformation may be performed in place. 52 + * 53 + * Caller must provide valid source and destination pointers. Be aware that 54 + * destination buffer will always be NULL-terminated. Source string must be 55 + * NULL-terminated as well. 56 + */ 57 + int string_unescape(char *src, char *dst, size_t size, unsigned int flags); 58 + 59 + static inline int string_unescape_inplace(char *buf, unsigned int flags) 60 + { 61 + return string_unescape(buf, buf, 0, flags); 62 + } 63 + 64 + static inline int string_unescape_any(char *src, char *dst, size_t size) 65 + { 66 + return string_unescape(src, dst, size, UNESCAPE_ANY); 67 + } 68 + 69 + static inline int string_unescape_any_inplace(char *buf) 70 + { 71 + return string_unescape_any(buf, buf, 0); 72 + } 73 + 16 74 #endif
+3
lib/Kconfig.debug
··· 1463 1463 1464 1464 source "lib/Kconfig.kmemcheck" 1465 1465 1466 + config TEST_STRING_HELPERS 1467 + tristate "Test functions located in the string_helpers module at runtime" 1468 + 1466 1469 config TEST_KSTRTOX 1467 1470 tristate "Test kstrto*() family of functions at runtime"
+3 -1
lib/Makefile
··· 22 22 23 23 obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ 24 24 bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ 25 - string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \ 25 + gcd.o lcm.o list_sort.o uuid.o flex_array.o \ 26 26 bsearch.o find_last_bit.o find_next_bit.o llist.o memweight.o kfifo.o 27 + obj-y += string_helpers.o 28 + obj-$(CONFIG_TEST_STRING_HELPERS) += test-string_helpers.o 27 29 obj-y += kstrtox.o 28 30 obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o 29 31
+133
lib/string_helpers.c
··· 2 2 * Helpers for formatting and printing strings 3 3 * 4 4 * Copyright 31 August 2008 James Bottomley 5 + * Copyright (C) 2013, Intel Corporation 5 6 */ 6 7 #include <linux/kernel.h> 7 8 #include <linux/math64.h> 8 9 #include <linux/export.h> 10 + #include <linux/ctype.h> 9 11 #include <linux/string_helpers.h> 10 12 11 13 /** ··· 68 66 return 0; 69 67 } 70 68 EXPORT_SYMBOL(string_get_size); 69 + 70 + static bool unescape_space(char **src, char **dst) 71 + { 72 + char *p = *dst, *q = *src; 73 + 74 + switch (*q) { 75 + case 'n': 76 + *p = '\n'; 77 + break; 78 + case 'r': 79 + *p = '\r'; 80 + break; 81 + case 't': 82 + *p = '\t'; 83 + break; 84 + case 'v': 85 + *p = '\v'; 86 + break; 87 + case 'f': 88 + *p = '\f'; 89 + break; 90 + default: 91 + return false; 92 + } 93 + *dst += 1; 94 + *src += 1; 95 + return true; 96 + } 97 + 98 + static bool unescape_octal(char **src, char **dst) 99 + { 100 + char *p = *dst, *q = *src; 101 + u8 num; 102 + 103 + if (isodigit(*q) == 0) 104 + return false; 105 + 106 + num = (*q++) & 7; 107 + while (num < 32 && isodigit(*q) && (q - *src < 3)) { 108 + num <<= 3; 109 + num += (*q++) & 7; 110 + } 111 + *p = num; 112 + *dst += 1; 113 + *src = q; 114 + return true; 115 + } 116 + 117 + static bool unescape_hex(char **src, char **dst) 118 + { 119 + char *p = *dst, *q = *src; 120 + int digit; 121 + u8 num; 122 + 123 + if (*q++ != 'x') 124 + return false; 125 + 126 + num = digit = hex_to_bin(*q++); 127 + if (digit < 0) 128 + return false; 129 + 130 + digit = hex_to_bin(*q); 131 + if (digit >= 0) { 132 + q++; 133 + num = (num << 4) | digit; 134 + } 135 + *p = num; 136 + *dst += 1; 137 + *src = q; 138 + return true; 139 + } 140 + 141 + static bool unescape_special(char **src, char **dst) 142 + { 143 + char *p = *dst, *q = *src; 144 + 145 + switch (*q) { 146 + case '\"': 147 + *p = '\"'; 148 + break; 149 + case '\\': 150 + *p = '\\'; 151 + break; 152 + case 'a': 153 + *p = '\a'; 154 + break; 155 + case 'e': 156 + *p = '\e'; 157 + break; 158 + default: 159 + return false; 160 + } 161 + *dst += 1; 162 + *src += 1; 163 + return true; 164 + } 165 + 166 + int string_unescape(char *src, char *dst, size_t size, unsigned int flags) 167 + { 168 + char *out = dst; 169 + 170 + while (*src && --size) { 171 + if (src[0] == '\\' && src[1] != '\0' && size > 1) { 172 + src++; 173 + size--; 174 + 175 + if (flags & UNESCAPE_SPACE && 176 + unescape_space(&src, &out)) 177 + continue; 178 + 179 + if (flags & UNESCAPE_OCTAL && 180 + unescape_octal(&src, &out)) 181 + continue; 182 + 183 + if (flags & UNESCAPE_HEX && 184 + unescape_hex(&src, &out)) 185 + continue; 186 + 187 + if (flags & UNESCAPE_SPECIAL && 188 + unescape_special(&src, &out)) 189 + continue; 190 + 191 + *out++ = '\\'; 192 + } 193 + *out++ = *src++; 194 + } 195 + *out = '\0'; 196 + 197 + return out - dst; 198 + } 199 + EXPORT_SYMBOL(string_unescape);
+103
lib/test-string_helpers.c
··· 1 + /* 2 + * Test cases for lib/string_helpers.c module. 3 + */ 4 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 5 + 6 + #include <linux/init.h> 7 + #include <linux/kernel.h> 8 + #include <linux/module.h> 9 + #include <linux/random.h> 10 + #include <linux/string.h> 11 + #include <linux/string_helpers.h> 12 + 13 + struct test_string { 14 + const char *in; 15 + const char *out; 16 + unsigned int flags; 17 + }; 18 + 19 + static const struct test_string strings[] __initconst = { 20 + { 21 + .in = "\\f\\ \\n\\r\\t\\v", 22 + .out = "\f\\ \n\r\t\v", 23 + .flags = UNESCAPE_SPACE, 24 + }, 25 + { 26 + .in = "\\40\\1\\387\\0064\\05\\040\\8a\\110\\777", 27 + .out = " \001\00387\0064\005 \\8aH?7", 28 + .flags = UNESCAPE_OCTAL, 29 + }, 30 + { 31 + .in = "\\xv\\xa\\x2c\\xD\\x6f2", 32 + .out = "\\xv\n,\ro2", 33 + .flags = UNESCAPE_HEX, 34 + }, 35 + { 36 + .in = "\\h\\\\\\\"\\a\\e\\", 37 + .out = "\\h\\\"\a\e\\", 38 + .flags = UNESCAPE_SPECIAL, 39 + }, 40 + }; 41 + 42 + static void __init test_string_unescape(unsigned int flags, bool inplace) 43 + { 44 + char in[256]; 45 + char out_test[256]; 46 + char out_real[256]; 47 + int i, p = 0, q_test = 0, q_real = sizeof(out_real); 48 + 49 + for (i = 0; i < ARRAY_SIZE(strings); i++) { 50 + const char *s = strings[i].in; 51 + int len = strlen(strings[i].in); 52 + 53 + /* Copy string to in buffer */ 54 + memcpy(&in[p], s, len); 55 + p += len; 56 + 57 + /* Copy expected result for given flags */ 58 + if (flags & strings[i].flags) { 59 + s = strings[i].out; 60 + len = strlen(strings[i].out); 61 + } 62 + memcpy(&out_test[q_test], s, len); 63 + q_test += len; 64 + } 65 + in[p++] = '\0'; 66 + 67 + /* Call string_unescape and compare result */ 68 + if (inplace) { 69 + memcpy(out_real, in, p); 70 + if (flags == UNESCAPE_ANY) 71 + q_real = string_unescape_any_inplace(out_real); 72 + else 73 + q_real = string_unescape_inplace(out_real, flags); 74 + } else if (flags == UNESCAPE_ANY) { 75 + q_real = string_unescape_any(in, out_real, q_real); 76 + } else { 77 + q_real = string_unescape(in, out_real, q_real, flags); 78 + } 79 + 80 + if (q_real != q_test || memcmp(out_test, out_real, q_test)) { 81 + pr_warn("Test failed: flags = %u\n", flags); 82 + print_hex_dump(KERN_WARNING, "Input: ", 83 + DUMP_PREFIX_NONE, 16, 1, in, p - 1, true); 84 + print_hex_dump(KERN_WARNING, "Expected: ", 85 + DUMP_PREFIX_NONE, 16, 1, out_test, q_test, true); 86 + print_hex_dump(KERN_WARNING, "Got: ", 87 + DUMP_PREFIX_NONE, 16, 1, out_real, q_real, true); 88 + } 89 + } 90 + 91 + static int __init test_string_helpers_init(void) 92 + { 93 + unsigned int i; 94 + 95 + pr_info("Running tests...\n"); 96 + for (i = 0; i < UNESCAPE_ANY + 1; i++) 97 + test_string_unescape(i, false); 98 + test_string_unescape(get_random_int() % (UNESCAPE_ANY + 1), true); 99 + 100 + return -EINVAL; 101 + } 102 + module_init(test_string_helpers_init); 103 + MODULE_LICENSE("Dual BSD/GPL");