aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2013-04-29 19:18:10 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-29 21:28:19 -0400
commit095d141b2e40665289d44a42d387ffac2841ef82 (patch)
tree3a70ce1eb403955647ab80d1089ea47e95afd9f3 /lib
parent30493cc9dddb68066dcc4878015660fdaa8e0965 (diff)
argv_split(): teach it to handle mutable strings
argv_split() allocates argv[count_argc(str)] array and assumes that it will find the same number of arguments later. This is obviously wrong if this string can be changed, say, by sysctl. With this patch argv_split() kstrndup's the whole string and does not split it, we simply replace the spaces with zeroes and keep the allocated memory in argv[-1] for argv_free(arg). We do not use argv[0] because: - str can be all-spaces or empty. In fact this case is fine, we could kfree() it before return, but: - str can have a space at the start, and we can not rely on kstrndup(skip_spaces(str)) because it can equally race if this string is mutable. Also, simplify count_argc() and kill the no longer used skip_arg(). Signed-off-by: Oleg Nesterov <oleg@redhat.com> Cc: Andi Kleen <andi@firstfloor.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/argv_split.c87
1 files changed, 40 insertions, 47 deletions
diff --git a/lib/argv_split.c b/lib/argv_split.c
index 1e9a6cbc3689..e927ed0e18a8 100644
--- a/lib/argv_split.c
+++ b/lib/argv_split.c
@@ -8,23 +8,17 @@
8#include <linux/slab.h> 8#include <linux/slab.h>
9#include <linux/export.h> 9#include <linux/export.h>
10 10
11static const char *skip_arg(const char *cp)
12{
13 while (*cp && !isspace(*cp))
14 cp++;
15
16 return cp;
17}
18
19static int count_argc(const char *str) 11static int count_argc(const char *str)
20{ 12{
21 int count = 0; 13 int count = 0;
14 bool was_space;
22 15
23 while (*str) { 16 for (was_space = true; *str; str++) {
24 str = skip_spaces(str); 17 if (isspace(*str)) {
25 if (*str) { 18 was_space = true;
19 } else if (was_space) {
20 was_space = false;
26 count++; 21 count++;
27 str = skip_arg(str);
28 } 22 }
29 } 23 }
30 24
@@ -39,10 +33,8 @@ static int count_argc(const char *str)
39 */ 33 */
40void argv_free(char **argv) 34void argv_free(char **argv)
41{ 35{
42 char **p; 36 argv--;
43 for (p = argv; *p; p++) 37 kfree(argv[0]);
44 kfree(*p);
45
46 kfree(argv); 38 kfree(argv);
47} 39}
48EXPORT_SYMBOL(argv_free); 40EXPORT_SYMBOL(argv_free);
@@ -59,43 +51,44 @@ EXPORT_SYMBOL(argv_free);
59 * considered to be a single argument separator. The returned array 51 * considered to be a single argument separator. The returned array
60 * is always NULL-terminated. Returns NULL on memory allocation 52 * is always NULL-terminated. Returns NULL on memory allocation
61 * failure. 53 * failure.
54 *
55 * The source string at `str' may be undergoing concurrent alteration via
56 * userspace sysctl activity (at least). The argv_split() implementation
57 * attempts to handle this gracefully by taking a local copy to work on.
62 */ 58 */
63char **argv_split(gfp_t gfp, const char *str, int *argcp) 59char **argv_split(gfp_t gfp, const char *str, int *argcp)
64{ 60{
65 int argc = count_argc(str); 61 char *argv_str;
66 char **argv = kzalloc(sizeof(*argv) * (argc+1), gfp); 62 bool was_space;
67 char **argvp; 63 char **argv, **argv_ret;
68 64 int argc;
69 if (argv == NULL) 65
70 goto out; 66 argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp);
71 67 if (!argv_str)
72 if (argcp) 68 return NULL;
73 *argcp = argc; 69
74 70 argc = count_argc(argv_str);
75 argvp = argv; 71 argv = kmalloc(sizeof(*argv) * (argc + 2), gfp);
76 72 if (!argv) {
77 while (*str) { 73 kfree(argv_str);
78 str = skip_spaces(str); 74 return NULL;
79 75 }
80 if (*str) {
81 const char *p = str;
82 char *t;
83
84 str = skip_arg(str);
85 76
86 t = kstrndup(p, str-p, gfp); 77 *argv = argv_str;
87 if (t == NULL) 78 argv_ret = ++argv;
88 goto fail; 79 for (was_space = true; *argv_str; argv_str++) {
89 *argvp++ = t; 80 if (isspace(*argv_str)) {
81 was_space = true;
82 *argv_str = 0;
83 } else if (was_space) {
84 was_space = false;
85 *argv++ = argv_str;
90 } 86 }
91 } 87 }
92 *argvp = NULL; 88 *argv = NULL;
93
94 out:
95 return argv;
96 89
97 fail: 90 if (argcp)
98 argv_free(argv); 91 *argcp = argc;
99 return NULL; 92 return argv_ret;
100} 93}
101EXPORT_SYMBOL(argv_split); 94EXPORT_SYMBOL(argv_split);