提交 095d141b 编写于 作者: O Oleg Nesterov 提交者: Linus Torvalds

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: NOleg Nesterov <oleg@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: NAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: NLinus Torvalds <torvalds@linux-foundation.org>
上级 30493cc9
...@@ -8,23 +8,17 @@ ...@@ -8,23 +8,17 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
static const char *skip_arg(const char *cp)
{
while (*cp && !isspace(*cp))
cp++;
return cp;
}
static int count_argc(const char *str) static int count_argc(const char *str)
{ {
int count = 0; int count = 0;
bool was_space;
while (*str) { for (was_space = true; *str; str++) {
str = skip_spaces(str); if (isspace(*str)) {
if (*str) { was_space = true;
} else if (was_space) {
was_space = false;
count++; count++;
str = skip_arg(str);
} }
} }
...@@ -39,10 +33,8 @@ static int count_argc(const char *str) ...@@ -39,10 +33,8 @@ static int count_argc(const char *str)
*/ */
void argv_free(char **argv) void argv_free(char **argv)
{ {
char **p; argv--;
for (p = argv; *p; p++) kfree(argv[0]);
kfree(*p);
kfree(argv); kfree(argv);
} }
EXPORT_SYMBOL(argv_free); EXPORT_SYMBOL(argv_free);
...@@ -59,43 +51,44 @@ EXPORT_SYMBOL(argv_free); ...@@ -59,43 +51,44 @@ EXPORT_SYMBOL(argv_free);
* considered to be a single argument separator. The returned array * considered to be a single argument separator. The returned array
* is always NULL-terminated. Returns NULL on memory allocation * is always NULL-terminated. Returns NULL on memory allocation
* failure. * failure.
*
* The source string at `str' may be undergoing concurrent alteration via
* userspace sysctl activity (at least). The argv_split() implementation
* attempts to handle this gracefully by taking a local copy to work on.
*/ */
char **argv_split(gfp_t gfp, const char *str, int *argcp) char **argv_split(gfp_t gfp, const char *str, int *argcp)
{ {
int argc = count_argc(str); char *argv_str;
char **argv = kzalloc(sizeof(*argv) * (argc+1), gfp); bool was_space;
char **argvp; char **argv, **argv_ret;
int argc;
if (argv == NULL)
goto out; argv_str = kstrndup(str, KMALLOC_MAX_SIZE - 1, gfp);
if (!argv_str)
if (argcp) return NULL;
*argcp = argc;
argc = count_argc(argv_str);
argvp = argv; argv = kmalloc(sizeof(*argv) * (argc + 2), gfp);
if (!argv) {
while (*str) { kfree(argv_str);
str = skip_spaces(str); return NULL;
}
if (*str) {
const char *p = str;
char *t;
str = skip_arg(str);
t = kstrndup(p, str-p, gfp); *argv = argv_str;
if (t == NULL) argv_ret = ++argv;
goto fail; for (was_space = true; *argv_str; argv_str++) {
*argvp++ = t; if (isspace(*argv_str)) {
was_space = true;
*argv_str = 0;
} else if (was_space) {
was_space = false;
*argv++ = argv_str;
} }
} }
*argvp = NULL; *argv = NULL;
out:
return argv;
fail: if (argcp)
argv_free(argv); *argcp = argc;
return NULL; return argv_ret;
} }
EXPORT_SYMBOL(argv_split); EXPORT_SYMBOL(argv_split);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册