diff options
| author | Oleg Nesterov <oleg@redhat.com> | 2013-04-29 19:18:10 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-29 21:28:19 -0400 |
| commit | 095d141b2e40665289d44a42d387ffac2841ef82 (patch) | |
| tree | 3a70ce1eb403955647ab80d1089ea47e95afd9f3 | |
| parent | 30493cc9dddb68066dcc4878015660fdaa8e0965 (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>
| -rw-r--r-- | lib/argv_split.c | 87 |
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 | ||
| 11 | static const char *skip_arg(const char *cp) | ||
| 12 | { | ||
| 13 | while (*cp && !isspace(*cp)) | ||
| 14 | cp++; | ||
| 15 | |||
| 16 | return cp; | ||
| 17 | } | ||
| 18 | |||
| 19 | static int count_argc(const char *str) | 11 | static 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 | */ |
| 40 | void argv_free(char **argv) | 34 | void 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 | } |
| 48 | EXPORT_SYMBOL(argv_free); | 40 | EXPORT_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 | */ |
| 63 | char **argv_split(gfp_t gfp, const char *str, int *argcp) | 59 | char **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 | } |
| 101 | EXPORT_SYMBOL(argv_split); | 94 | EXPORT_SYMBOL(argv_split); |
