308 lines
7.9 KiB
Plaintext
308 lines
7.9 KiB
Plaintext
/*
|
|
* Copyright (c) 1992 William Jolitz. All rights reserved.
|
|
* Written by William Jolitz 1/92
|
|
*
|
|
* Redistribution and use in source and binary forms are freely permitted
|
|
* provided that the above copyright notice and attribution and date of work
|
|
* and this paragraph are duplicated in all such forms.
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* This procedure implements a minimal program execution facility for
|
|
* 386BSD. It interfaces to the BSD kernel as the execve system call.
|
|
* Significant limitations and lack of compatiblity with POSIX are
|
|
* present with this version, to make its basic operation more clear.
|
|
*
|
|
*/
|
|
|
|
#include "param.h"
|
|
#include "systm.h"
|
|
#include "proc.h"
|
|
#include "mount.h"
|
|
#include "namei.h"
|
|
#include "vnode.h"
|
|
#include "file.h"
|
|
#include "exec.h"
|
|
#include "stat.h"
|
|
#include "wait.h"
|
|
#include "signalvar.h"
|
|
#include "mman.h"
|
|
|
|
#include "vm/vm.h"
|
|
#include "vm/vm_param.h"
|
|
#include "vm/vm_map.h"
|
|
#include "vm/vm_kern.h"
|
|
|
|
#include "machine/reg.h"
|
|
|
|
static char rcsid[] = "$Header: /cvsroot/src/sys/kern/Attic/kern_execve,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $";
|
|
|
|
/*
|
|
* Bill's first-cut execve() system call. Puts hair on your chest.
|
|
*/
|
|
|
|
/* ARGSUSED */
|
|
execve(p, uap, retval)
|
|
struct proc *p;
|
|
register struct args {
|
|
char *fname;
|
|
char **argp;
|
|
char **envp;
|
|
} *uap;
|
|
int *retval;
|
|
{
|
|
register struct nameidata *ndp;
|
|
int rv, amt;
|
|
struct nameidata nd;
|
|
struct exec hdr;
|
|
char **kargbuf, **kargbufp, *kstringbuf, *kstringbufp;
|
|
char **org, **vectp, *ep;
|
|
u_int needsenv, limitonargs, stringlen;
|
|
int addr, size;
|
|
int argc;
|
|
char *cp;
|
|
struct stat statb;
|
|
struct vmspace *vs;
|
|
int tsize, dsize, bsize, cnt, foff;
|
|
|
|
/*
|
|
* Step 1. Lookup filename to see if we have something to execute.
|
|
*/
|
|
ndp = &nd;
|
|
ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW;
|
|
ndp->ni_segflg = UIO_USERSPACE;
|
|
ndp->ni_dirp = uap->fname;
|
|
|
|
/* is it there? */
|
|
if (rv = namei(ndp, p))
|
|
return (rv);
|
|
|
|
/* is it a regular file? */
|
|
if (ndp->ni_vp->v_type != VREG) {
|
|
vput(ndp->ni_vp);
|
|
return(ENOEXEC);
|
|
}
|
|
|
|
/* is it executable? */
|
|
rv = VOP_ACCESS(ndp->ni_vp, VEXEC, p->p_ucred, p);
|
|
if (rv)
|
|
goto exec_fail;
|
|
|
|
rv = vn_stat(ndp->ni_vp, &statb, p);
|
|
if (rv)
|
|
goto exec_fail;
|
|
|
|
/*
|
|
* Step 2. Does the file contain a format we can
|
|
* understand and execute
|
|
*/
|
|
rv = vn_rdwr(UIO_READ, ndp->ni_vp, (caddr_t)&hdr, sizeof(hdr),
|
|
0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &amt, p);
|
|
|
|
/* big enough to hold a header? */
|
|
if (rv)
|
|
goto exec_fail;
|
|
|
|
/* that we recognize? */
|
|
rv = ENOEXEC;
|
|
if (hdr.a_magic != ZMAGIC)
|
|
goto exec_fail;
|
|
|
|
/* sanity check "ain't not such thing as a sanity clause" -groucho */
|
|
if (/*hdr.a_text == 0 || */ hdr.a_text > MAXTSIZ
|
|
|| hdr.a_text % NBPG || hdr.a_text > statb.st_size)
|
|
goto exec_fail;
|
|
|
|
if (hdr.a_data == 0 || hdr.a_data > DFLDSIZ
|
|
|| hdr.a_data > statb.st_size
|
|
|| hdr.a_data + hdr.a_text > statb.st_size)
|
|
goto exec_fail;
|
|
|
|
if (hdr.a_bss > MAXDSIZ)
|
|
goto exec_fail;
|
|
|
|
if (hdr.a_text + hdr.a_data + hdr.a_bss > MAXTSIZ + MAXDSIZ)
|
|
goto exec_fail;
|
|
|
|
/*
|
|
* Step 3. File and header are valid. Now, dig out the strings
|
|
* out of the old process image.
|
|
*/
|
|
|
|
/* assumption: most execve's have less than 256 arguments, with a
|
|
* total of string storage space not exceeding 2K. It is more
|
|
* frequent that when this fails, string space falls short first
|
|
* (e.g. as when a large termcap environment variable is present).
|
|
* It is infrequent when more than 256 arguments are used that take
|
|
* up less than 2K of space (e.g. args average more than 8 chars).
|
|
*
|
|
* What we give up in this implementation is a dense encoding of
|
|
* the data structure in the receiving program's address space.
|
|
* This means that there is plenty of wasted space (up to 6KB)
|
|
* as the price we pay for a fast, single pass algorithm.
|
|
*
|
|
* Our alternative would be to accumulate strings and pointers
|
|
* in the first pass, then, knowing the sizes and number of the
|
|
* strings, pack them neatly and tightly togeither in the second
|
|
* pass. This means two copies of the strings, and string copying
|
|
* is much of the cost of exec.
|
|
*/
|
|
|
|
/* allocate string buffer and arg buffer */
|
|
org = kargbuf = (char **) kmem_alloc_wait(exec_map,
|
|
(NCARGS + PAGE_SIZE)/PAGE_SIZE);
|
|
kstringbuf = kstringbufp = ((char *)kargbuf) + NBPG/2;
|
|
kargbuf += NBPG/(4*sizeof(int));
|
|
kargbufp = kargbuf;
|
|
|
|
/* first, do args */
|
|
needsenv = 1;
|
|
vectp = uap->argp;
|
|
|
|
do_env_as_well:
|
|
cnt = 0;
|
|
/* for each envp, copy in string */
|
|
limitonargs = NCARGS;
|
|
if(vectp == 0) goto dont_bother;
|
|
do {
|
|
/* did we outgrow initial argbuf, if so, die */
|
|
if (kargbufp == (char **)kstringbuf)
|
|
goto exec_fail;
|
|
|
|
/* get an string pointer */
|
|
ep = (char *)fuword(vectp++);
|
|
if (ep == (char *)-1) {
|
|
rv = EFAULT;
|
|
goto exec_fail;
|
|
}
|
|
|
|
/* if not null pointer, copy in string */
|
|
if (ep) {
|
|
if (rv = copyinstr(ep, kstringbufp, limitonargs,
|
|
&stringlen)) goto exec_fail;
|
|
/* assume that strings usually all fit in last page */
|
|
*kargbufp = (char *)(kstringbufp - kstringbuf
|
|
+ USRSTACK - NBPG + NBPG/2);
|
|
kargbufp++;
|
|
cnt++;
|
|
kstringbufp += stringlen;
|
|
limitonargs -= stringlen + sizeof(long);
|
|
} else {
|
|
*kargbufp++ = 0;
|
|
limitonargs -= sizeof(long);
|
|
break;
|
|
}
|
|
} while (limitonargs > 0);
|
|
|
|
dont_bother:
|
|
if (limitonargs <= 0) {
|
|
rv = E2BIG;
|
|
goto exec_fail;
|
|
}
|
|
|
|
if (needsenv) {
|
|
argc = cnt;
|
|
vectp = uap->envp;
|
|
needsenv = 0;
|
|
goto do_env_as_well;
|
|
}
|
|
|
|
kargbuf[-1] = (char *)argc;
|
|
|
|
/*
|
|
* Step 4. Build the new processes image.
|
|
*/
|
|
|
|
/* At this point, we are committed -- destroy old executable */
|
|
vs = p->p_vmspace;
|
|
addr = 0;
|
|
size = USRSTACK - addr;
|
|
/* blow away all address space */
|
|
rv = vm_deallocate(&vs->vm_map, addr, size, FALSE);
|
|
if (rv)
|
|
goto exec_abort;
|
|
|
|
/* build a new address space */
|
|
addr = 0;
|
|
if (hdr.a_text == 0) {
|
|
/* screwball mode */
|
|
foff = tsize = 0;
|
|
hdr.a_data += hdr.a_text;
|
|
} else {
|
|
tsize = roundup(hdr.a_text, NBPG);
|
|
foff = NBPG;
|
|
}
|
|
dsize = roundup(hdr.a_data, NBPG);
|
|
bsize = roundup(hdr.a_bss + dsize, NBPG);
|
|
bsize -= dsize;
|
|
|
|
/* map text & data*/
|
|
rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL,
|
|
MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff);
|
|
if (rv)
|
|
goto exec_abort;
|
|
|
|
/* r/w data, ro text */
|
|
if (tsize) {
|
|
addr = 0;
|
|
rv = vm_protect(&vs->vm_map, addr, tsize, FALSE, VM_PROT_READ|VM_PROT_EXECUTE);
|
|
if (rv)
|
|
goto exec_abort;
|
|
}
|
|
|
|
/* create anonymous memory region for bss */
|
|
addr = dsize + tsize;
|
|
rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE);
|
|
if (rv)
|
|
goto exec_abort;
|
|
|
|
/* create anonymous memory region for stack */
|
|
addr = USRSTACK - MAXSSIZ;
|
|
rv = vm_allocate(&vs->vm_map, &addr, MAXSSIZ, FALSE);
|
|
if (rv)
|
|
goto exec_abort;
|
|
|
|
/*
|
|
* Step 5. Prepare process for execution.
|
|
*/
|
|
|
|
/* touchup process information */
|
|
vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */
|
|
vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */
|
|
vs->vm_ssize = MAXSSIZ/NBPG; /* stack size (pages) */
|
|
vs->vm_taddr = 0; /* user virtual address of text XXX */
|
|
vs->vm_daddr = (caddr_t)tsize; /* user virtual address of data XXX */
|
|
/* user VA at max stack growth */
|
|
vs->vm_maxsaddr = (caddr_t)(USRSTACK - MAXSSIZ);
|
|
|
|
/* everything fits in a single page, no fixups, no more work */
|
|
/* (groan) due to bug in vm_map_copy, can't remap. copy for now. */
|
|
rv = copyout((caddr_t)org, (caddr_t)USRSTACK - NBPG, NBPG);
|
|
if(rv)
|
|
goto exec_abort;
|
|
|
|
/* close files on exec, fixup signals */
|
|
fdcloseexec(p);
|
|
execsigs(p);
|
|
|
|
p->p_regs[SP] = USRSTACK - NBPG + NBPG/4 - 4;
|
|
vs->vm_ssize = 1; /* stack size (pages) */
|
|
setregs(p, hdr.a_entry);
|
|
kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE);
|
|
vput(ndp->ni_vp);
|
|
return (0);
|
|
|
|
exec_fail:
|
|
vput(ndp->ni_vp);
|
|
return(rv);
|
|
|
|
exec_abort:
|
|
/* untested and probably bogus */
|
|
kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE);
|
|
vput(ndp->ni_vp);
|
|
exit(p, W_EXITCODE(0, SIGABRT));
|
|
return(0);
|
|
|
|
}
|