aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>2006-11-25 14:09:39 -0500
committerLinus Torvalds <torvalds@woody.osdl.org>2006-11-25 16:28:34 -0500
commit5d48545e5e88ab7a27ba6a5cb1e8fff617754b61 (patch)
tree2da1a8d8e1ca4088cd91cc080f424b3e25e9423f /arch
parent9dce447a542d8b4bedf13d6a4c4fc6737240372e (diff)
[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>
Diffstat (limited to 'arch')
-rw-r--r--arch/um/include/os.h2
-rw-r--r--arch/um/os-Linux/Makefile10
-rw-r--r--arch/um/os-Linux/execvp.c149
-rw-r--r--arch/um/os-Linux/helper.c14
4 files changed, 166 insertions, 9 deletions
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 6516f6dca96d..13a86bd383d3 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -233,6 +233,8 @@ extern unsigned long __do_user_copy(void *to, const void *from, int n,
233 void (*op)(void *to, const void *from, 233 void (*op)(void *to, const void *from,
234 int n), int *faulted_out); 234 int n), int *faulted_out);
235 235
236/* execvp.c */
237extern int execvp_noalloc(char *buf, const char *file, char *const argv[]);
236/* helper.c */ 238/* helper.c */
237extern int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, 239extern int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
238 unsigned long *stack_out); 240 unsigned long *stack_out);
diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile
index b4183929b32c..2f8c79464015 100644
--- a/arch/um/os-Linux/Makefile
+++ b/arch/um/os-Linux/Makefile
@@ -3,8 +3,8 @@
3# Licensed under the GPL 3# Licensed under the GPL
4# 4#
5 5
6obj-y = aio.o elf_aux.o file.o helper.o irq.o main.o mem.o process.o sigio.o \ 6obj-y = aio.o elf_aux.o execvp.o file.o helper.o irq.o main.o mem.o process.o \
7 signal.o start_up.o time.o trap.o tty.o uaccess.o umid.o tls.o \ 7 sigio.o signal.o start_up.o time.o trap.o tty.o uaccess.o umid.o tls.o \
8 user_syms.o util.o drivers/ sys-$(SUBARCH)/ 8 user_syms.o util.o drivers/ sys-$(SUBARCH)/
9 9
10obj-$(CONFIG_MODE_SKAS) += skas/ 10obj-$(CONFIG_MODE_SKAS) += skas/
@@ -15,9 +15,9 @@ user-objs-$(CONFIG_MODE_TT) += tt.o
15obj-$(CONFIG_TTY_LOG) += tty_log.o 15obj-$(CONFIG_TTY_LOG) += tty_log.o
16user-objs-$(CONFIG_TTY_LOG) += tty_log.o 16user-objs-$(CONFIG_TTY_LOG) += tty_log.o
17 17
18USER_OBJS := $(user-objs-y) aio.o elf_aux.o file.o helper.o irq.o main.o mem.o \ 18USER_OBJS := $(user-objs-y) aio.o elf_aux.o execvp.o file.o helper.o irq.o \
19 process.o sigio.o signal.o start_up.o time.o trap.o tty.o tls.o \ 19 main.o mem.o process.o sigio.o signal.o start_up.o time.o trap.o tty.o \
20 uaccess.o umid.o util.o 20 tls.o uaccess.o umid.o util.o
21 21
22CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH) 22CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH)
23 23
diff --git a/arch/um/os-Linux/execvp.c b/arch/um/os-Linux/execvp.c
new file mode 100644
index 000000000000..66e583a4031b
--- /dev/null
+++ b/arch/um/os-Linux/execvp.c
@@ -0,0 +1,149 @@
1/* Copyright (C) 2006 by Paolo Giarrusso - modified from glibc' execvp.c.
2 Original copyright notice follows:
3
4 Copyright (C) 1991,92,1995-99,2002,2004 Free Software Foundation, Inc.
5 This file is part of the GNU C Library.
6
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, write to the Free
19 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA. */
21#include <unistd.h>
22
23#include <stdbool.h>
24#include <stdlib.h>
25#include <string.h>
26#include <errno.h>
27#include <limits.h>
28
29#ifndef TEST
30#include "um_malloc.h"
31#else
32#include <stdio.h>
33#define um_kmalloc malloc
34#endif
35#include "os.h"
36
37/* Execute FILE, searching in the `PATH' environment variable if it contains
38 no slashes, with arguments ARGV and environment from `environ'. */
39int execvp_noalloc(char *buf, const char *file, char *const argv[])
40{
41 if (*file == '\0') {
42 return -ENOENT;
43 }
44
45 if (strchr (file, '/') != NULL) {
46 /* Don't search when it contains a slash. */
47 execv(file, argv);
48 } else {
49 int got_eacces;
50 size_t len, pathlen;
51 char *name, *p;
52 char *path = getenv("PATH");
53 if (path == NULL)
54 path = ":/bin:/usr/bin";
55
56 len = strlen(file) + 1;
57 pathlen = strlen(path);
58 /* Copy the file name at the top. */
59 name = memcpy(buf + pathlen + 1, file, len);
60 /* And add the slash. */
61 *--name = '/';
62
63 got_eacces = 0;
64 p = path;
65 do {
66 char *startp;
67
68 path = p;
69 //Let's avoid this GNU extension.
70 //p = strchrnul (path, ':');
71 p = strchr(path, ':');
72 if (!p)
73 p = strchr(path, '\0');
74
75 if (p == path)
76 /* Two adjacent colons, or a colon at the beginning or the end
77 of `PATH' means to search the current directory. */
78 startp = name + 1;
79 else
80 startp = memcpy(name - (p - path), path, p - path);
81
82 /* Try to execute this name. If it works, execv will not return. */
83 execv(startp, argv);
84
85 /*
86 if (errno == ENOEXEC) {
87 }
88 */
89
90 switch (errno) {
91 case EACCES:
92 /* Record the we got a `Permission denied' error. If we end
93 up finding no executable we can use, we want to diagnose
94 that we did find one but were denied access. */
95 got_eacces = 1;
96 case ENOENT:
97 case ESTALE:
98 case ENOTDIR:
99 /* Those errors indicate the file is missing or not executable
100 by us, in which case we want to just try the next path
101 directory. */
102 case ENODEV:
103 case ETIMEDOUT:
104 /* Some strange filesystems like AFS return even
105 stranger error numbers. They cannot reasonably mean
106 anything else so ignore those, too. */
107 case ENOEXEC:
108 /* We won't go searching for the shell
109 * if it is not executable - the Linux
110 * kernel already handles this enough,
111 * for us. */
112 break;
113
114 default:
115 /* Some other error means we found an executable file, but
116 something went wrong executing it; return the error to our
117 caller. */
118 return -errno;
119 }
120 } while (*p++ != '\0');
121
122 /* We tried every element and none of them worked. */
123 if (got_eacces)
124 /* At least one failure was due to permissions, so report that
125 error. */
126 return -EACCES;
127 }
128
129 /* Return the error from the last attempt (probably ENOENT). */
130 return -errno;
131}
132#ifdef TEST
133int main(int argc, char**argv)
134{
135 char buf[PATH_MAX];
136 int ret;
137 argc--;
138 if (!argc) {
139 fprintf(stderr, "Not enough arguments\n");
140 return 1;
141 }
142 argv++;
143 if (ret = execvp_noalloc(buf, argv[0], argv)) {
144 errno = -ret;
145 perror("execvp_noalloc");
146 }
147 return 0;
148}
149#endif
diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c
index d13299cfa318..c7ad6306e22f 100644
--- a/arch/um/os-Linux/helper.c
+++ b/arch/um/os-Linux/helper.c
@@ -8,18 +8,21 @@
8#include <unistd.h> 8#include <unistd.h>
9#include <errno.h> 9#include <errno.h>
10#include <sched.h> 10#include <sched.h>
11#include <limits.h>
11#include <sys/signal.h> 12#include <sys/signal.h>
12#include <sys/wait.h> 13#include <sys/wait.h>
13#include "user.h" 14#include "user.h"
14#include "kern_util.h" 15#include "kern_util.h"
15#include "user_util.h" 16#include "user_util.h"
16#include "os.h" 17#include "os.h"
18#include "um_malloc.h"
17 19
18struct helper_data { 20struct helper_data {
19 void (*pre_exec)(void*); 21 void (*pre_exec)(void*);
20 void *pre_data; 22 void *pre_data;
21 char **argv; 23 char **argv;
22 int fd; 24 int fd;
25 char *buf;
23}; 26};
24 27
25/* Debugging aid, changed only from gdb */ 28/* Debugging aid, changed only from gdb */
@@ -41,9 +44,8 @@ static int helper_child(void *arg)
41 } 44 }
42 if (data->pre_exec != NULL) 45 if (data->pre_exec != NULL)
43 (*data->pre_exec)(data->pre_data); 46 (*data->pre_exec)(data->pre_data);
44 execvp(argv[0], argv); 47 errval = execvp_noalloc(data->buf, argv[0], argv);
45 errval = -errno; 48 printk("helper_child - execvp of '%s' failed - errno = %d\n", argv[0], -errval);
46 printk("helper_child - execve of '%s' failed - errno = %d\n", argv[0], errno);
47 os_write_file(data->fd, &errval, sizeof(errval)); 49 os_write_file(data->fd, &errval, sizeof(errval));
48 kill(os_getpid(), SIGKILL); 50 kill(os_getpid(), SIGKILL);
49 return 0; 51 return 0;
@@ -84,11 +86,13 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
84 data.pre_data = pre_data; 86 data.pre_data = pre_data;
85 data.argv = argv; 87 data.argv = argv;
86 data.fd = fds[1]; 88 data.fd = fds[1];
89 data.buf = __cant_sleep() ? um_kmalloc_atomic(PATH_MAX) :
90 um_kmalloc(PATH_MAX);
87 pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data); 91 pid = clone(helper_child, (void *) sp, CLONE_VM | SIGCHLD, &data);
88 if (pid < 0) { 92 if (pid < 0) {
89 ret = -errno; 93 ret = -errno;
90 printk("run_helper : clone failed, errno = %d\n", errno); 94 printk("run_helper : clone failed, errno = %d\n", errno);
91 goto out_close; 95 goto out_free2;
92 } 96 }
93 97
94 close(fds[1]); 98 close(fds[1]);
@@ -109,6 +113,8 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv,
109 CATCH_EINTR(waitpid(pid, NULL, 0)); 113 CATCH_EINTR(waitpid(pid, NULL, 0));
110 } 114 }
111 115
116out_free2:
117 kfree(data.buf);
112out_close: 118out_close:
113 if (fds[1] != -1) 119 if (fds[1] != -1)
114 close(fds[1]); 120 close(fds[1]);