1 /* Copyright (C) 2000, 2011 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <http://www.gnu.org/licenses/>.  */
17 
18 #include <errno.h>
19 #include <alloca.h>
20 #include <unistd.h>
21 #include <signal.h>
22 #include <stdbool.h>
23 #include <sys/syscall.h>
24 #include <fcntl.h>
25 
26 #include <sys/resource.h>
27 #include <not-cancel.h>
28 #include <internal-signals.h>
29 
30 #include <spawn.h>
31 #include "spawn_int.h"
32 
33 /* The Unix standard contains a long explanation of the way to signal
34    an error after the fork() was successful.  Since no new wait status
35    was wanted there is no way to signal an error using one of the
36    available methods.  The committee chose to signal an error by a
37    normal program exit with the exit code 127.  */
38 #define SPAWN_ERROR	127
39 
40 /* Execute file actions.
41  * Returns true on error.
42  */
execute_file_actions(const posix_spawn_file_actions_t * fa)43 inline static bool execute_file_actions(const posix_spawn_file_actions_t *fa)
44 {
45 	struct rlimit64 fdlimit;
46 	bool have_fdlimit = false;
47 	int cnt;
48 
49 	for (cnt = 0; cnt < fa->__used; ++cnt) {
50 		struct __spawn_action *action = &fa->__actions[cnt];
51 
52 		switch (action->tag) {
53 		case spawn_do_close:
54 			if (close_not_cancel(action->action.close_action.fd) != 0) {
55 				if (!have_fdlimit) {
56 					getrlimit64(RLIMIT_NOFILE, &fdlimit);
57 					have_fdlimit = true;
58 				}
59 
60 				/* Only signal errors for file descriptors out of range.  */
61 				if (0 > action->action.close_action.fd
62 				    || action->action.close_action.fd >= fdlimit.rlim_cur)
63 					/* Signal the error.  */
64 					return true;
65 			}
66 			break;
67 
68 		case spawn_do_open:;
69 			int new_fd = open_not_cancel(action->action.open_action.path,
70 						     action->action.open_action.oflag
71 						     | O_LARGEFILE,
72 						     action->action.open_action.mode);
73 
74 			if (new_fd == -1)
75 				return true;
76 
77 			/* Make sure the desired file descriptor is used.  */
78 			if (new_fd != action->action.open_action.fd) {
79 				if (dup2(new_fd, action->action.open_action.fd)
80 				    != action->action.open_action.fd)
81 					return true;
82 
83 				if (close_not_cancel(new_fd) != 0)
84 					return true;
85 			}
86 			break;
87 
88 		case spawn_do_dup2:
89 			if (dup2(action->action.dup2_action.fd,
90 				   action->action.dup2_action.newfd)
91 			    != action->action.dup2_action.newfd)
92 				return true;
93 			break;
94 		}
95 	}
96 
97 	return false;
98 }
99 
100 #define DANGEROUS (POSIX_SPAWN_SETSIGMASK		\
101 		   | POSIX_SPAWN_SETSIGDEF		\
102 		   | POSIX_SPAWN_SETSCHEDPARAM		\
103 		   | POSIX_SPAWN_SETSCHEDULER		\
104 		   | POSIX_SPAWN_SETPGROUP		\
105 		   | POSIX_SPAWN_RESETIDS)
is_vfork_safe(short int flags)106 inline static bool is_vfork_safe(short int flags)
107 {
108 	return ((flags & POSIX_SPAWN_USEVFORK) || !(flags & DANGEROUS));
109 }
110 
111 
112 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
113    Before running the process perform the actions described in FILE-ACTIONS. */
114 static int
__spawni(pid_t * pid,const char * file,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[],const char * path)115 __spawni(pid_t *pid, const char *file,
116 	 const posix_spawn_file_actions_t *fa,
117 	 const posix_spawnattr_t *attrp, char *const argv[],
118 	 char *const envp[], const char *path)
119 {
120 	short int flags = attrp ? attrp->__flags : 0;
121 
122 	pid_t new_pid;
123 	if (is_vfork_safe(flags) && !fa)
124 		new_pid = vfork();
125 	else {
126 #ifdef __ARCH_USE_MMU__
127 		new_pid = fork();
128 #else
129 		return ENOSYS;
130 #endif
131 	}
132 
133 	if (new_pid) {
134 		if (new_pid < 0)
135 			return errno;
136 
137 		if (pid)
138 			*pid = new_pid;
139 
140 		return 0;
141 	}
142 
143 	if (flags & POSIX_SPAWN_SETSIGMASK) {
144 		if (sigprocmask(SIG_SETMASK, &attrp->__ss, NULL) != 0)
145 			goto error;
146 	}
147 
148 	if (flags & POSIX_SPAWN_SETSIGDEF) {
149 		/* We have to iterate over all signals.  This could possibly be
150 		   done better but it requires system specific solutions since
151 		   the sigset_t data type can be very different on different
152 		   architectures.  */
153 		struct sigaction sa;
154 		int sig;
155 
156 		memset(&sa, 0, sizeof(sa));
157 
158 		sigset_t hset;
159 		sigprocmask (SIG_BLOCK, 0, &hset);
160 
161 		for (sig = 1; sig < _NSIG; ++sig) {
162 		  if ((flags & POSIX_SPAWN_SETSIGDEF)
163 		  && sigismember (&attrp->__sd, sig))
164 		  {
165 		    sa.sa_handler = SIG_DFL;
166 		  }
167 	          else if (sigismember (&hset, sig))
168 		  {
169 		    if (__is_internal_signal (sig))
170 		      sa.sa_handler = SIG_IGN;
171 		    else
172 		    {
173 		      __libc_sigaction (sig, 0, &sa);
174 		      if (sa.sa_handler == SIG_IGN)
175 			continue;
176 		      sa.sa_handler = SIG_DFL;
177 		    }
178 		  }
179 	        else
180 		  continue;
181 
182 		__libc_sigaction (sig, &sa, 0);
183 		}
184 	}
185 
186 	if (flags & POSIX_SPAWN_SETSCHEDULER) {
187 		if (sched_setscheduler(0, attrp->__policy, &attrp->__sp) == -1)
188 			goto error;
189 	} else if (flags & POSIX_SPAWN_SETSCHEDPARAM) {
190 		if (sched_setparam(0, &attrp->__sp) == -1)
191 			goto error;
192 	}
193 
194 	if (flags & POSIX_SPAWN_SETPGROUP) {
195 		if (setpgid(0, attrp->__pgrp) != 0)
196 			goto error;
197 	}
198 
199 	if (flags & POSIX_SPAWN_RESETIDS) {
200 		if (seteuid(getuid()) || setegid(getgid()))
201 			goto error;
202 	}
203 
204 	if (fa && execute_file_actions(fa))
205 		goto error;
206 
207 	if (!path || strchr(file, '/')) {
208 		execve(file, argv, envp);
209 		goto error;
210 	}
211 
212 
213 	char *name;
214 	{
215 		size_t filelen = strlen(file) + 1;
216 		size_t pathlen = strlen(path) + 1;
217 		name = alloca(pathlen + filelen);
218 
219 		/* Copy the file name at the top. */
220 		name = (char *) memcpy(name + pathlen, file, filelen);
221 
222 		/* And add the slash.  */
223 		*--name = '/';
224 	}
225 
226 	char *p = (char *)path;
227 	do {
228 		char *startp;
229 		path = p;
230 		p = strchrnul(path, ':');
231 
232 		/* Two adjacent colons, or a colon at the beginning or the end
233 		   of `PATH' means to search the current directory.  */
234 		if (p == path)
235 			startp = name + 1;
236 		else
237 			startp = (char *) memcpy(name - (p - path), path, p - path);
238 
239 		execve(startp, argv, envp);
240 
241 		switch (errno) {
242 		case EACCES:
243 		case ENOENT:
244 		case ESTALE:
245 		case ENOTDIR:
246 			/* Those errors indicate the file is missing or not
247 			   executable by us, in which case we want to just try
248 			   the next path directory. */
249 			break;
250 		default:
251 			/* Some other error means we found an executable file,
252 			   but something went wrong executing it; return the
253 			   error to our caller. */
254 			goto error;
255 		}
256 
257 	} while (*p++ != '\0');
258 
259 error:
260 	_exit(SPAWN_ERROR);
261 }
262 
263 /* Spawn a new process executing PATH with the attributes describes in *ATTRP.
264    Before running the process perform the actions described in FILE-ACTIONS. */
posix_spawn(pid_t * pid,const char * path,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[])265 int posix_spawn (pid_t *pid, const char *path,
266 	       const posix_spawn_file_actions_t *fa,
267 	       const posix_spawnattr_t *attrp, char *const argv[],
268 	       char *const envp[])
269 {
270 	return __spawni(pid, path, fa, attrp, argv, envp, NULL);
271 }
272 
273 /* Spawn a new process executing FILE with the attributes describes in *ATTRP.
274    Before running the process perform the actions described in FILE-ACTIONS. */
275 int
posix_spawnp(pid_t * pid,const char * file,const posix_spawn_file_actions_t * fa,const posix_spawnattr_t * attrp,char * const argv[],char * const envp[])276 posix_spawnp(pid_t *pid, const char *file,
277 	     const posix_spawn_file_actions_t *fa,
278 	     const posix_spawnattr_t *attrp, char *const argv[],
279 	     char *const envp[])
280 {
281 	const char *path = getenv("PATH");
282 
283 	if (!path)
284 		path = ":/bin:/usr/bin";
285 
286 	return __spawni(pid, file, fa, attrp, argv, envp, path);
287 }
288