Commit a whole cluster of last minute critical (and one cosmetic) fixes
[unix-history] / sys / kern / kern_execve.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1993, David Greenman
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by David Greenman
16 * 4. The name of the developer may be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $Id: kern_execve.c,v 1.19 1994/03/21 09:35:30 davidg Exp $
32 */
33
34#include "param.h"
35#include "systm.h"
36#include "signalvar.h"
37#include "resourcevar.h"
38#include "imgact.h"
39#include "kernel.h"
40#include "mount.h"
41#include "file.h"
42#include "acct.h"
43#include "exec.h"
44#include "stat.h"
45#include "wait.h"
46#include "mman.h"
47#include "malloc.h"
48#include "syslog.h"
49
50#include "vm/vm.h"
51#include "vm/vm_param.h"
52#include "vm/vm_map.h"
53#include "vm/vm_kern.h"
54#include "vm/vm_user.h"
55
56#include "machine/reg.h"
57
58int exec_extract_strings __P((struct image_params *));
59int *exec_copyout_strings __P((struct image_params *));
60
61/*
62 * execsw_set is constructed for us by the linker. Each of the items
63 * is a pointer to a `const struct execsw', hence the double pointer here.
64 */
65extern const struct linker_set execsw_set;
66const struct execsw **execsw = (const struct execsw **)&execsw_set.ls_items[0];
67
68/*
69 * execve() system call.
70 */
71int
72execve(p, uap, retval)
73 struct proc *p;
74 register struct execve_args *uap;
75 int *retval;
76{
77 struct nameidata nd, *ndp;
78 char *stringbase, *stringp;
79 int *stack_base;
80 int error, resid, len, i;
81 struct image_params image_params, *iparams;
82 struct vnode *vnodep;
83 struct vattr attr;
84 char *image_header;
85
86 iparams = &image_params;
87 bzero((caddr_t)iparams, sizeof(struct image_params));
88 image_header = (char *)0;
89
90 /*
91 * Initialize a few constants in the common area
92 */
93 iparams->proc = p;
94 iparams->uap = uap;
95 iparams->attr = &attr;
96
97 /*
98 * Allocate temporary demand zeroed space for argument and
99 * environment strings
100 */
101 error = vm_allocate(kernel_map, (vm_offset_t *)&iparams->stringbase,
102 ARG_MAX, TRUE);
103 if (error) {
104 log(LOG_WARNING, "execve: failed to allocate string space\n");
105 return (error);
106 }
107
108 if (!iparams->stringbase) {
109 error = ENOMEM;
110 goto exec_fail;
111 }
112 iparams->stringp = iparams->stringbase;
113 iparams->stringspace = ARG_MAX;
114
115 /*
116 * Translate the file name. namei() returns a vnode pointer
117 * in ni_vp amoung other things.
118 */
119 ndp = &nd;
120 ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME;
121 ndp->ni_segflg = UIO_USERSPACE;
122 ndp->ni_dirp = uap->fname;
123
124interpret:
125
126 error = namei(ndp, p);
127 if (error) {
128 vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
129 ARG_MAX);
130 goto exec_fail;
131 }
132
133 iparams->vnodep = vnodep = ndp->ni_vp;
134
135 if (vnodep == NULL) {
136 error = ENOEXEC;
137 goto exec_fail_dealloc;
138 }
139
140 /*
141 * Check file permissions (also 'opens' file)
142 */
143 error = exec_check_permissions(iparams);
144 if (error)
145 goto exec_fail_dealloc;
146
147 /*
148 * Map the image header (first page) of the file into
149 * kernel address space
150 */
151 error = vm_mmap(kernel_map, /* map */
152 (vm_offset_t *)&image_header, /* address */
153 PAGE_SIZE, /* size */
154 VM_PROT_READ, /* protection */
155 VM_PROT_READ, /* max protection */
156 MAP_FILE, /* flags */
157 (caddr_t)vnodep, /* vnode */
158 0); /* offset */
159 if (error) {
160 uprintf("mmap failed: %d\n",error);
161 goto exec_fail_dealloc;
162 }
163 iparams->image_header = image_header;
164
165 /*
166 * Loop through list of image activators, calling each one.
167 * If there is no match, the activator returns -1. If there
168 * is a match, but there was an error during the activation,
169 * the error is returned. Otherwise 0 means success. If the
170 * image is interpreted, loop back up and try activating
171 * the interpreter.
172 */
173 for (i = 0; execsw[i]; ++i) {
174 if (execsw[i]->ex_imgact)
175 error = (*execsw[i]->ex_imgact)(iparams);
176 else
177 continue;
178
179 if (error == -1)
180 continue;
181 if (error)
182 goto exec_fail_dealloc;
183 if (iparams->interpreted) {
184 /* free old vnode and name buffer */
185 vput(ndp->ni_vp);
186 FREE(ndp->ni_pnbuf, M_NAMEI);
187 if (vm_deallocate(kernel_map,
188 (vm_offset_t)image_header, PAGE_SIZE))
189 panic("execve: header dealloc failed (1)");
190
191 /* set new name to that of the interpreter */
192 ndp->ni_segflg = UIO_SYSSPACE;
193 ndp->ni_dirp = iparams->interpreter_name;
194 ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME;
195 goto interpret;
196 }
197 break;
198 }
199 /* If we made it through all the activators and none matched, exit. */
200 if (error == -1) {
201 error = ENOEXEC;
202 goto exec_fail_dealloc;
203 }
204
205 /*
206 * Copy out strings (args and env) and initialize stack base
207 */
208 stack_base = exec_copyout_strings(iparams);
209 p->p_vmspace->vm_minsaddr = (char *)stack_base;
210
211 /*
212 * Stuff argument count as first item on stack
213 */
214 *(--stack_base) = iparams->argc;
215
216 /* close files on exec */
217 fdcloseexec(p);
218
219 /* reset caught signals */
220 execsigs(p);
221
222 /* name this process - nameiexec(p, ndp) */
223 len = MIN(ndp->ni_namelen,MAXCOMLEN);
224 bcopy(ndp->ni_ptr, p->p_comm, len);
225 p->p_comm[len] = 0;
226
227 /*
228 * mark as executable, wakeup any process that was vforked and tell
229 * it that it now has it's own resources back
230 */
231 p->p_flag |= SEXEC;
232 if (p->p_pptr && (p->p_flag & SPPWAIT)) {
233 p->p_flag &= ~SPPWAIT;
234 wakeup((caddr_t)p->p_pptr);
235 }
236
237 /* implement set userid/groupid */
238 p->p_flag &= ~SUGID;
239
240 /*
241 * Turn off kernel tracing for set-id programs, except for
242 * root.
243 */
244 if (p->p_tracep && (attr.va_mode & (VSUID | VSGID)) &&
245 suser(p->p_ucred, &p->p_acflag)) {
246 p->p_traceflag = 0;
247 vrele(p->p_tracep);
248 p->p_tracep = 0;
249 }
250 if ((attr.va_mode & VSUID) && (p->p_flag & STRC) == 0) {
251 p->p_ucred = crcopy(p->p_ucred);
252 p->p_ucred->cr_uid = attr.va_uid;
253 p->p_flag |= SUGID;
254 }
255 if ((attr.va_mode & VSGID) && (p->p_flag & STRC) == 0) {
256 p->p_ucred = crcopy(p->p_ucred);
257 p->p_ucred->cr_groups[0] = attr.va_gid;
258 p->p_flag |= SUGID;
259 }
260
261 /*
262 * Implement correct POSIX saved uid behavior.
263 */
264 p->p_cred->p_svuid = p->p_ucred->cr_uid;
265 p->p_cred->p_svgid = p->p_ucred->cr_gid;
266
267 /* mark vnode pure text */
268 ndp->ni_vp->v_flag |= VTEXT;
269
270 /*
271 * If tracing the process, trap to debugger so breakpoints
272 * can be set before the program executes.
273 */
274 if (p->p_flag & STRC)
275 psignal(p, SIGTRAP);
276
277 /* clear "fork but no exec" flag, as we _are_ execing */
278 p->p_acflag &= ~AFORK;
279
280 /* Set entry address */
281 setregs(p, iparams->entry_addr, stack_base);
282
283 /*
284 * free various allocated resources
285 */
286 if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase, ARG_MAX))
287 panic("execve: string buffer dealloc failed (1)");
288 if (vm_deallocate(kernel_map, (vm_offset_t)image_header, PAGE_SIZE))
289 panic("execve: header dealloc failed (2)");
290 vput(ndp->ni_vp);
291 FREE(ndp->ni_pnbuf, M_NAMEI);
292
293 return (0);
294
295exec_fail_dealloc:
296 if (iparams->stringbase && iparams->stringbase != (char *)-1)
297 if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
298 ARG_MAX))
299 panic("execve: string buffer dealloc failed (2)");
300 if (iparams->image_header && iparams->image_header != (char *)-1)
301 if (vm_deallocate(kernel_map,
302 (vm_offset_t)iparams->image_header, PAGE_SIZE))
303 panic("execve: header dealloc failed (3)");
304 vput(ndp->ni_vp);
305 FREE(ndp->ni_pnbuf, M_NAMEI);
306
307exec_fail:
308 if (iparams->vmspace_destroyed) {
309 /* sorry, no more process anymore. exit gracefully */
310#if 0 /* XXX */
311 vm_deallocate(&vs->vm_map, USRSTACK - MAXSSIZ, MAXSSIZ);
312#endif
313 kexit(p, W_EXITCODE(0, SIGABRT));
314 /* NOT REACHED */
315 return(0);
316 } else {
317 return(error);
318 }
319}
320
321/*
322 * Destroy old address space, and allocate a new stack
323 * The new stack is only SGROWSIZ large because it is grown
324 * automatically in trap.c.
325 */
326int
327exec_new_vmspace(iparams)
328 struct image_params *iparams;
329{
330 int error;
331 struct vmspace *vmspace = iparams->proc->p_vmspace;
332 caddr_t stack_addr = (caddr_t) (USRSTACK - SGROWSIZ);
333
334 iparams->vmspace_destroyed = 1;
335
336 /* Blow away entire process VM */
337 vm_deallocate(&vmspace->vm_map, 0, USRSTACK);
338
339 /* Allocate a new stack */
340 error = vm_allocate(&vmspace->vm_map, (vm_offset_t *)&stack_addr,
341 SGROWSIZ, FALSE);
342 if (error)
343 return(error);
344
345 vmspace->vm_ssize = SGROWSIZ >> PAGE_SHIFT;
346
347 /* Initialize maximum stack address */
348 vmspace->vm_maxsaddr = (char *)USRSTACK - MAXSSIZ;
349
350 return(0);
351}
352
353/*
354 * Copy out argument and environment strings from the old process
355 * address space into the temporary string buffer.
356 */
357int
358exec_extract_strings(iparams)
359 struct image_params *iparams;
360{
361 char **argv, **envv;
362 char *argp, *envp;
363 int length;
364
365 /*
366 * extract arguments first
367 */
368
369 argv = iparams->uap->argv;
370
371 if (argv)
372 while (argp = (caddr_t) fuword(argv++)) {
373 if (argp == (caddr_t) -1)
374 return (EFAULT);
375 if (copyinstr(argp, iparams->stringp, iparams->stringspace,
376 &length) == ENAMETOOLONG)
377 return(E2BIG);
378 iparams->stringspace -= length;
379 iparams->stringp += length;
380 iparams->argc++;
381 }
382
383 /*
384 * extract environment strings
385 */
386
387 envv = iparams->uap->envv;
388
389 if (envv)
390 while (envp = (caddr_t) fuword(envv++)) {
391 if (envp == (caddr_t) -1)
392 return (EFAULT);
393 if (copyinstr(envp, iparams->stringp, iparams->stringspace,
394 &length) == ENAMETOOLONG)
395 return(E2BIG);
396 iparams->stringspace -= length;
397 iparams->stringp += length;
398 iparams->envc++;
399 }
400
401 return (0);
402}
403
404/*
405 * Copy strings out to the new process address space, constructing
406 * new arg and env vector tables. Return a pointer to the base
407 * so that it can be used as the initial stack pointer.
408 */
409int *
410exec_copyout_strings(iparams)
411 struct image_params *iparams;
412{
413 int argc, envc;
414 char **vectp;
415 char *stringp, *destp;
416 int *stack_base;
417 int vect_table_size, string_table_size;
418
419 /*
420 * Calculate string base and vector table pointers.
421 */
422 destp = (caddr_t) ((caddr_t)USRSTACK -
423 roundup((ARG_MAX - iparams->stringspace), sizeof(char *)));
424 /*
425 * The '+ 2' is for the null pointers at the end of each of the
426 * arg and env vector sets
427 */
428 vectp = (char **) (destp -
429 (iparams->argc + iparams->envc + 2) * sizeof(char *));
430
431 /*
432 * vectp also becomes our initial stack base
433 */
434 stack_base = (int *)vectp;
435
436 stringp = iparams->stringbase;
437 argc = iparams->argc;
438 envc = iparams->envc;
439
440 for (; argc > 0; --argc) {
441 *(vectp++) = destp;
442 while (*destp++ = *stringp++);
443 }
444
445 /* a null vector table pointer seperates the argp's from the envp's */
446 *(vectp++) = NULL;
447
448 for (; envc > 0; --envc) {
449 *(vectp++) = destp;
450 while (*destp++ = *stringp++);
451 }
452
453 /* end of vector table is a null pointer */
454 *vectp = NULL;
455
456 return (stack_base);
457}
458
459/*
460 * Check permissions of file to execute.
461 * Return 0 for success or error code on failure.
462 */
463int
464exec_check_permissions(iparams)
465 struct image_params *iparams;
466{
467 struct proc *p = iparams->proc;
468 struct vnode *vnodep = iparams->vnodep;
469 struct vattr *attr = iparams->attr;
470 int error;
471
472 /*
473 * Check number of open-for-writes on the file and deny execution
474 * if there are any.
475 */
476 if (vnodep->v_writecount) {
477 return (ETXTBSY);
478 }
479
480 /* Get file attributes */
481 error = VOP_GETATTR(vnodep, attr, p->p_ucred, p);
482 if (error)
483 return (error);
484
485 /*
486 * 1) Check if file execution is disabled for the filesystem that this
487 * file resides on.
488 * 2) Insure that at least one execute bit is on - otherwise root
489 * will always succeed, and we don't want to happen unless the
490 * file really is executable.
491 * 3) Insure that the file is a regular file.
492 */
493 if ((vnodep->v_mount->mnt_flag & MNT_NOEXEC) ||
494 ((attr->va_mode & 0111) == 0) ||
495 (attr->va_type != VREG)) {
496 return (EACCES);
497 }
498
499 /*
500 * Zero length files can't be exec'd
501 */
502 if (attr->va_size == 0)
503 return (ENOEXEC);
504
505 /*
506 * Disable setuid/setgid if the filesystem prohibits it or if
507 * the process is being traced.
508 */
509 if ((vnodep->v_mount->mnt_flag & MNT_NOSUID) || (p->p_flag & STRC))
510 attr->va_mode &= ~(VSUID | VSGID);
511
512 /*
513 * Check for execute permission to file based on current credentials.
514 * Then call filesystem specific open routine (which does nothing
515 * in the general case).
516 */
517 error = VOP_ACCESS(vnodep, VEXEC, p->p_ucred, p);
518 if (error)
519 return (error);
520
521 error = VOP_OPEN(vnodep, FREAD, p->p_ucred, p);
522 if (error)
523 return (error);
524
525 return (0);
526}