This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / sys / kern / kern_execve.c
CommitLineData
15637ed4 1/*
317350b1 2 * Copyright (c) 1993, David Greenman
15637ed4
RG
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:
317350b1
DG
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.
15637ed4 18 *
317350b1 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15637ed4
RG
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
67c9e20b 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
15637ed4
RG
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 *
05d53854 31 * $Id: kern_execve.c,v 1.19 1994/03/21 09:35:30 davidg Exp $
15637ed4
RG
32 */
33
34#include "param.h"
35#include "systm.h"
36#include "signalvar.h"
37#include "resourcevar.h"
317350b1 38#include "imgact.h"
3228baa0 39#include "kernel.h"
15637ed4 40#include "mount.h"
15637ed4 41#include "file.h"
7d69de6f 42#include "acct.h"
15637ed4
RG
43#include "exec.h"
44#include "stat.h"
45#include "wait.h"
46#include "mman.h"
47#include "malloc.h"
317350b1 48#include "syslog.h"
15637ed4
RG
49
50#include "vm/vm.h"
51#include "vm/vm_param.h"
52#include "vm/vm_map.h"
53#include "vm/vm_kern.h"
fde1aeb2 54#include "vm/vm_user.h"
15637ed4
RG
55
56#include "machine/reg.h"
57
317350b1 58int exec_extract_strings __P((struct image_params *));
65e8cb73 59int *exec_copyout_strings __P((struct image_params *));
317350b1 60
3228baa0
GW
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];
15637ed4
RG
67
68/*
69 * execve() system call.
70 */
4c45483e 71int
15637ed4
RG
72execve(p, uap, retval)
73 struct proc *p;
3c7eb27c 74 register struct execve_args *uap;
15637ed4
RG
75 int *retval;
76{
317350b1 77 struct nameidata nd, *ndp;
65e8cb73
DG
78 char *stringbase, *stringp;
79 int *stack_base;
317350b1 80 int error, resid, len, i;
317350b1
DG
81 struct image_params image_params, *iparams;
82 struct vnode *vnodep;
15637ed4 83 struct vattr attr;
317350b1
DG
84 char *image_header;
85
86 iparams = &image_params;
87 bzero((caddr_t)iparams, sizeof(struct image_params));
88 image_header = (char *)0;
15637ed4
RG
89
90 /*
317350b1
DG
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 */
fde1aeb2
GW
101 error = vm_allocate(kernel_map, (vm_offset_t *)&iparams->stringbase,
102 ARG_MAX, TRUE);
317350b1
DG
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.
15637ed4
RG
118 */
119 ndp = &nd;
317350b1 120 ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME;
15637ed4
RG
121 ndp->ni_segflg = UIO_USERSPACE;
122 ndp->ni_dirp = uap->fname;
123
317350b1 124interpret:
15637ed4 125
317350b1
DG
126 error = namei(ndp, p);
127 if (error) {
fde1aeb2
GW
128 vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
129 ARG_MAX);
317350b1 130 goto exec_fail;
15637ed4 131 }
15637ed4 132
317350b1 133 iparams->vnodep = vnodep = ndp->ni_vp;
15637ed4 134
317350b1
DG
135 if (vnodep == NULL) {
136 error = ENOEXEC;
137 goto exec_fail_dealloc;
15637ed4
RG
138 }
139
140 /*
317350b1 141 * Check file permissions (also 'opens' file)
15637ed4 142 */
317350b1
DG
143 error = exec_check_permissions(iparams);
144 if (error)
145 goto exec_fail_dealloc;
15637ed4 146
317350b1
DG
147 /*
148 * Map the image header (first page) of the file into
149 * kernel address space
150 */
fde1aeb2
GW
151 error = vm_mmap(kernel_map, /* map */
152 (vm_offset_t *)&image_header, /* address */
05d53854 153 PAGE_SIZE, /* size */
fde1aeb2
GW
154 VM_PROT_READ, /* protection */
155 VM_PROT_READ, /* max protection */
156 MAP_FILE, /* flags */
157 (caddr_t)vnodep, /* vnode */
158 0); /* offset */
317350b1 159 if (error) {
67c9e20b 160 uprintf("mmap failed: %d\n",error);
317350b1 161 goto exec_fail_dealloc;
15637ed4 162 }
317350b1
DG
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 */
3228baa0
GW
173 for (i = 0; execsw[i]; ++i) {
174 if (execsw[i]->ex_imgact)
175 error = (*execsw[i]->ex_imgact)(iparams);
317350b1
DG
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 */
33f2dd50
DG
185 vput(ndp->ni_vp);
186 FREE(ndp->ni_pnbuf, M_NAMEI);
fde1aeb2 187 if (vm_deallocate(kernel_map,
05d53854 188 (vm_offset_t)image_header, PAGE_SIZE))
317350b1 189 panic("execve: header dealloc failed (1)");
33f2dd50 190
317350b1 191 /* set new name to that of the interpreter */
33f2dd50 192 ndp->ni_segflg = UIO_SYSSPACE;
317350b1
DG
193 ndp->ni_dirp = iparams->interpreter_name;
194 ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW | SAVENAME;
195 goto interpret;
15637ed4 196 }
317350b1
DG
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;
15637ed4
RG
203 }
204
317350b1
DG
205 /*
206 * Copy out strings (args and env) and initialize stack base
207 */
208 stack_base = exec_copyout_strings(iparams);
65e8cb73 209 p->p_vmspace->vm_minsaddr = (char *)stack_base;
15637ed4 210
317350b1
DG
211 /*
212 * Stuff argument count as first item on stack
213 */
65e8cb73 214 *(--stack_base) = iparams->argc;
15637ed4 215
05d53854 216 /* close files on exec */
317350b1 217 fdcloseexec(p);
05d53854
DG
218
219 /* reset caught signals */
317350b1 220 execsigs(p);
15637ed4 221
317350b1
DG
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;
15637ed4
RG
226
227 /*
317350b1
DG
228 * mark as executable, wakeup any process that was vforked and tell
229 * it that it now has it's own resources back
15637ed4 230 */
317350b1
DG
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 */
d03b71c0
GW
238 p->p_flag &= ~SUGID;
239
24b27a4b
GW
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 }
05d53854 250 if ((attr.va_mode & VSUID) && (p->p_flag & STRC) == 0) {
317350b1 251 p->p_ucred = crcopy(p->p_ucred);
9e85cc83 252 p->p_ucred->cr_uid = attr.va_uid;
d03b71c0 253 p->p_flag |= SUGID;
317350b1 254 }
05d53854 255 if ((attr.va_mode & VSGID) && (p->p_flag & STRC) == 0) {
317350b1 256 p->p_ucred = crcopy(p->p_ucred);
9e85cc83 257 p->p_ucred->cr_groups[0] = attr.va_gid;
d03b71c0 258 p->p_flag |= SUGID;
317350b1
DG
259 }
260
9e85cc83
GW
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
317350b1
DG
267 /* mark vnode pure text */
268 ndp->ni_vp->v_flag |= VTEXT;
15637ed4
RG
269
270 /*
317350b1
DG
271 * If tracing the process, trap to debugger so breakpoints
272 * can be set before the program executes.
15637ed4 273 */
317350b1
DG
274 if (p->p_flag & STRC)
275 psignal(p, SIGTRAP);
15637ed4 276
317350b1
DG
277 /* clear "fork but no exec" flag, as we _are_ execing */
278 p->p_acflag &= ~AFORK;
15637ed4 279
317350b1 280 /* Set entry address */
65e8cb73 281 setregs(p, iparams->entry_addr, stack_base);
15637ed4 282
317350b1
DG
283 /*
284 * free various allocated resources
285 */
05d53854 286 if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase, ARG_MAX))
317350b1 287 panic("execve: string buffer dealloc failed (1)");
05d53854 288 if (vm_deallocate(kernel_map, (vm_offset_t)image_header, PAGE_SIZE))
317350b1
DG
289 panic("execve: header dealloc failed (2)");
290 vput(ndp->ni_vp);
291 FREE(ndp->ni_pnbuf, M_NAMEI);
15637ed4 292
317350b1 293 return (0);
15637ed4 294
317350b1
DG
295exec_fail_dealloc:
296 if (iparams->stringbase && iparams->stringbase != (char *)-1)
fde1aeb2
GW
297 if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
298 ARG_MAX))
317350b1
DG
299 panic("execve: string buffer dealloc failed (2)");
300 if (iparams->image_header && iparams->image_header != (char *)-1)
fde1aeb2 301 if (vm_deallocate(kernel_map,
05d53854 302 (vm_offset_t)iparams->image_header, PAGE_SIZE))
317350b1
DG
303 panic("execve: header dealloc failed (3)");
304 vput(ndp->ni_vp);
305 FREE(ndp->ni_pnbuf, M_NAMEI);
15637ed4 306
317350b1
DG
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);
15637ed4 318 }
317350b1 319}
15637ed4 320
317350b1
DG
321/*
322 * Destroy old address space, and allocate a new stack
4c321944 323 * The new stack is only SGROWSIZ large because it is grown
317350b1
DG
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;
4c321944 332 caddr_t stack_addr = (caddr_t) (USRSTACK - SGROWSIZ);
15637ed4 333
317350b1 334 iparams->vmspace_destroyed = 1;
15637ed4 335
317350b1
DG
336 /* Blow away entire process VM */
337 vm_deallocate(&vmspace->vm_map, 0, USRSTACK);
15637ed4 338
317350b1 339 /* Allocate a new stack */
fde1aeb2 340 error = vm_allocate(&vmspace->vm_map, (vm_offset_t *)&stack_addr,
4c321944 341 SGROWSIZ, FALSE);
317350b1
DG
342 if (error)
343 return(error);
15637ed4 344
4c321944
DG
345 vmspace->vm_ssize = SGROWSIZ >> PAGE_SHIFT;
346
317350b1
DG
347 /* Initialize maximum stack address */
348 vmspace->vm_maxsaddr = (char *)USRSTACK - MAXSSIZ;
15637ed4 349
317350b1
DG
350 return(0);
351}
15637ed4 352
317350b1
DG
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;
33f2dd50 364
317350b1
DG
365 /*
366 * extract arguments first
367 */
33f2dd50 368
317350b1
DG
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 }
15637ed4 382
317350b1
DG
383 /*
384 * extract environment strings
385 */
15637ed4 386
317350b1
DG
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 }
33f2dd50 400
317350b1
DG
401 return (0);
402}
15637ed4 403
317350b1
DG
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 */
65e8cb73 409int *
317350b1
DG
410exec_copyout_strings(iparams)
411 struct image_params *iparams;
412{
413 int argc, envc;
414 char **vectp;
65e8cb73
DG
415 char *stringp, *destp;
416 int *stack_base;
317350b1 417 int vect_table_size, string_table_size;
15637ed4
RG
418
419 /*
317350b1
DG
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
15637ed4 427 */
317350b1
DG
428 vectp = (char **) (destp -
429 (iparams->argc + iparams->envc + 2) * sizeof(char *));
15637ed4 430
317350b1
DG
431 /*
432 * vectp also becomes our initial stack base
433 */
65e8cb73 434 stack_base = (int *)vectp;
15637ed4 435
317350b1
DG
436 stringp = iparams->stringbase;
437 argc = iparams->argc;
438 envc = iparams->envc;
15637ed4 439
317350b1
DG
440 for (; argc > 0; --argc) {
441 *(vectp++) = destp;
442 while (*destp++ = *stringp++);
15637ed4 443 }
317350b1
DG
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++);
15637ed4
RG
451 }
452
317350b1
DG
453 /* end of vector table is a null pointer */
454 *vectp = NULL;
15637ed4 455
317350b1
DG
456 return (stack_base);
457}
15637ed4 458
317350b1
DG
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;
15637ed4 471
317350b1
DG
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 }
15637ed4 479
317350b1
DG
480 /* Get file attributes */
481 error = VOP_GETATTR(vnodep, attr, p->p_ucred, p);
482 if (error)
483 return (error);
15637ed4 484
317350b1
DG
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 }
15637ed4 498
20fcbdf2
DG
499 /*
500 * Zero length files can't be exec'd
501 */
502 if (attr->va_size == 0)
503 return (ENOEXEC);
504
317350b1
DG
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);
15637ed4 511
317350b1
DG
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);
15637ed4 520
317350b1
DG
521 error = VOP_OPEN(vnodep, FREAD, p->p_ucred, p);
522 if (error)
523 return (error);
15637ed4 524
317350b1 525 return (0);
15637ed4 526}