[PATCH] uml: make execvp safe for our usage

Reimplement execvp for our purposes - after we call fork() it is fundamentally
unsafe to use the kernel allocator - current is not valid there.  So we simply
pass to our modified execvp() a preallocated buffer.  This fixes a real bug
and works very well in testing (I've seen indirectly warning messages from the
forked thread - they went on the pipe connected to its stdout and where read
as a number by UML, when calling read_output().  I verified the obtained
number corresponded to "BUG:").

The added use of __cant_sleep() is not a new bug since __cant_sleep() is
already used in the same function - passing an atomicity parameter would be
better but it would require huge change, stating that this function must not
be called in atomic context and can sleep is a better idea (will make sure of
this gradually).

Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Acked-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c
index d13299c..c7ad630 100644
--- a/arch/um/os-Linux/helper.c
+++ b/arch/um/os-Linux/helper.c
@@ -8,18 +8,21 @@
 #include <unistd.h>
 #include <errno.h>
 #include <sched.h>
+#include <limits.h>
 #include <sys/signal.h>
 #include <sys/wait.h>
 #include "user.h"
 #include "kern_util.h"
 #include "user_util.h"
 #include "os.h"
+#include "um_malloc.h"
 
 struct helper_data {
 	void (*pre_exec)(void*);
 	void *pre_data;
 	char **argv;
 	int fd;
+	char *buf;
 };
 
 /* Debugging aid, changed only from gdb */
@@ -41,9 +44,8 @@
 	}
 	if (data->pre_exec != NULL)
 		(*data->pre_exec)(data->pre_data);
-	execvp(argv[0], argv);
-	errval = -errno;
-	printk("helper_child - execve of '%s' failed - errno = %d\n", argv[0], errno);
+	errval = execvp_noalloc(data->buf, argv[0], argv);
+	printk("helper_child - execvp of '%s' failed - errno = %d\n", argv[0], -errval);
 	os_write_file(data->fd, &errval, sizeof(errval));
 	kill(os_getpid(), SIGKILL);
 	return 0;
@@ -84,11 +86,13 @@
 	data.pre_data = pre_data;
 	data.argv = argv;
 	data.fd = fds[1];
+	data.buf = __cant_sleep() ? um_kmalloc_atomic(PATH_MAX) :
+					um_kmalloc(PATH_MAX);
 	pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data);
 	if (pid < 0) {
 		ret = -errno;
 		printk("run_helper : clone failed, errno = %d\n", errno);
-		goto out_close;
+		goto out_free2;
 	}
 
 	close(fds[1]);
@@ -109,6 +113,8 @@
 		CATCH_EINTR(waitpid(pid, NULL, 0));
 	}
 
+out_free2:
+	kfree(data.buf);
 out_close:
 	if (fds[1] != -1)
 		close(fds[1]);