Source to kern/kern_execve


Enter a symbol's name here to quickly find it.

/*
 * 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: /usr/bill/working/sys/kern/RCS/kern_execve.c,v 1.3 92/01/21 21:29:13 william 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);

}