2021-10-11 22:19:34 -03:00

1190 lines
34 KiB
C

static char sccsid[] = "@(#)48 1.112.1.47 src/bos/kernel/proc/exec.c, sysproc, bos41J, 9516B_all 4/21/95 10:51:25";
/*
* COMPONENT_NAME: SYSPROC
*
* FUNCTIONS: copyargs_in
* copyargs_out
* execvex
*
* ORIGINS: 3, 26, 27, 83
*
*
* This module contains IBM CONFIDENTIAL code. -- (IBM
* Confidential Restricted when combined with the aggregated
* modules for this product)
* SOURCE MATERIALS
*
* (C) COPYRIGHT International Business Machines Corp. 1988, 1995
* All Rights Reserved
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
*
* NOTES:
* This is the platform-independent part of exec[2]. It calls
* machine-dependent functions to create and destroy process
* images.
*/
/*
* LEVEL 1, 5 Years Bull Confidential Information
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/audit.h>
#include <sys/acct.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/fp_io.h>
#include <sys/ldr.h>
#include <sys/lock.h>
#include <sys/lockl.h>
#include <sys/malloc.h>
#include <sys/mstsave.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/pseg.h> /* for stack size calculations */
#include <sys/seg.h> /* for stack size calculations */
#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/sysinfo.h>
#include <sys/syspest.h>
#include <sys/systm.h>
#include <sys/trchkid.h>
#include <sys/user.h>
#include <sys/lockname.h>
#include <sys/sleep.h>
#include <sys/atomic_op.h>
#include <a.out.h>
#include <sys/resource.h>
#include "swapper_data.h"
#include "exec.h" /* local header file for exec */
#include "m_exec.h" /* machine-dependent local header */
#define MAX_LARGE_DATA_SET_MODEL 0x80000000 /* 8 times 256M-bytes */
/*
* function declarations
*/
static int copyargs_in(); /* copy arguments into kernel memory */
static int copyargs_out(); /* copy arguments to top of stack */
extern int exec_priv(); /* privilege amplification */
extern void bcopy(); /* block copy */
extern void fs_exec(); /* close requested files */
extern void execexit(); /* special syscall exit for execve() */
extern int setregs(); /* set up user machine context */
extern void prof_off(); /* turns off profiling */
BUGVDEF(excdbg, 0); /* debugging control variable */
/*
* NAME: execvex
*
* FUNCTION: exec[2] system call handler
*
* This is the system call handler for all the variations of
* exec[2]. The library routines for execl(), execle(),
* execlp(), execv(), and execvp() all transform their arguments
* into the execve() form, and make this system call.
*
* This is the machine-independent part of exec. It calls
* machine-dependent functions in m_exec.c, which resides
* in the platfrom-dependent source tree.
*
* EXECUTION ENVIRONMENT:
* preemptable (except during file system calls)
* may page fault
*
* NOTES:
*
* High-level flow of exec processing (when successful):
*
* * open the exec'ed file
* * read the load module header and check the magic number
* * if "#!", open the indirect shell file and read its header
* * copy arguments and environment variables from old process image
* * load the exec'ed program
* * copy arguments and environment to new process image
* * update u-block and proc structure for new process image
* * call execexit() to start up the new program
*
* RETURNS: -1 if unsuccessful, u.u_error has the reason;
* Does not return when successful, instead calls execexit()
* to start the new program at its main entry point.
*/
execvex(const char *fname, char *const argp[], char *const envp[])
/* fname name of file to exec */
/* argp[] array of ptrs to exec arguments */
/* envp[] array of ptrs to environment variables */
{
struct file *fp, *fp_indirect=0;/* open file table pointer */
int bcount; /* byte count from fp_read() */
int oflags; /* open mode flags */
int rc; /* return code from fp_read() */
struct rlimit alim; /* used for setrlimit() */
long newlimit; /* used for call to ulimit() */
lock_t lockt; /* return code from lockl() */
/* automatic structure declarations */
struct stat stst; /* stat structure */
union execunionX hdr; /* exec file header */
struct xargs xargs; /* exec argument structure */
char auditName[MAXCOMLEN+1]; /* base file name */
register struct xargs *xp; /* pointer to xargs */
static int svcnum = 0;
int svcrc = 0;
/* Position of the pointer to the LIBPATH variable in the envp array */
int libpath_ptr;
/* Pointer to loader domain/LIBPATH variable string */
char *ldomain_path = NULL;
int env_len = 0; /* Total length of env. strings */
int arg_len = 0; /* Total length of arg. strings */
/* Buffer for the error messages used by ld_emessdump()
* in case of an error during load.
*/
char *errmsg_buf = NULL;
int ipri; /* saved interrupt priority */
struct thread *t, *th; /* current thread */
struct thread *tzomb = NULL; /*zombie threads in the process*/
struct proc *p; /* current process */
char *errorp; /* current error storage */
t = curthread;
p = t->t_procp;
errorp = &t->t_uthreadp->ut_error;
/* Await death of all other threads belonging to the process */
if (p->p_threadcount > 1 || t->t_uthreadp != &uthr0)
{
ipri = disable_lock(INTMAX, &proc_base_lock);
#ifdef _POWER_MP
simple_lock(&proc_int_lock);
#endif
/*
* fork, exec, and exit are serialized by the forkstack. If
* we have gotten this far, then a pending fork/exit in the
* same process will be negated by setting STERM.
*/
p->p_flag |= (SEXECING);
p->p_int |= (STERM);
if (p->p_active > 1)
{
/* The process may be partially stopped/suspended */
cont(p);
/* The process may be partially swapped out */
schedsig(p);
t->t_flags |= TTERM;
e_wakeup((int *)&p->p_synch); /* STERM set */
e_wakeup((int *)&t->t_synch); /* TTERM set */
/* Wake up the threads sleeping interruptibly */
th = t->t_nextthread; /* skip oneself */
while (th != t) {
switch (th->t_wtype) {
case TWCPU :
case TNOWAIT :
break;
case TWPGIN :
vcs_interrupt(th);
break;
default :
if (th->t_flags & TWAKEONSIG) {
if (th->t_state == TSSLEEP)
setrq(th, E_WKX_PREEMPT, RQHEAD);
else
th->t_wtype = TNOWAIT;
}
break;
}
th = th->t_nextthread;
}
/* Wait for the other threads to terminate themselves */
p->p_suspended++;
while (p->p_suspended < p->p_active) {
#ifdef _POWER_MP
simple_unlock(&proc_int_lock);
#endif
e_sleep_thread((int *)&p->p_synch,
&proc_base_lock, LOCK_HANDLER);
#ifdef _POWER_MP
simple_lock(&proc_int_lock);
#endif
}
p->p_suspended--;
ASSERT(p->p_active == 1);
}
/* Orphan all other threads belonging to the process */
if (p->p_threadcount > 1)
{
/* Keep anchors */
p->p_threadlist = t;
tzomb = t->t_nextthread;
/* Remove oneself from the fellow zombies */
tzomb->t_prevthread = t->t_prevthread;
t->t_prevthread->t_nextthread = tzomb;
/* Remove the zombies from the threadlist */
t->t_nextthread = t->t_prevthread = t;
#ifdef DEBUG
/* Check the zombies */
th = tzomb;
do {
switch (th->t_state) {
case TSZOMB:
case TSIDL:
p->p_threadcount--;
break;
default :
assert(FALSE);
}
th = th->t_nextthread;
} while (th != tzomb);
assert(p->p_threadcount == 1);
#else
p->p_threadcount = 1;
#endif
}
/* Switch to the standard uthread area if not already there */
if (t->t_uthreadp != &uthr0)
{
/* copy the current uthread */
bcopy(t->t_uthreadp, &uthr0, sizeof(struct uthread));
/* switch */
t->t_uthreadp = &uthr0;
SET_CSA(&uthr0.ut_save);
}
/* Reset so that subsequent sleeps will not return early. */
p->p_int &= ~STERM;
t->t_flags &= ~TTERM;
#ifdef _POWER_MP
simple_unlock(&proc_int_lock);
#endif
unlock_enable(ipri, &proc_base_lock);
}
else {
p->p_flag |= (SEXECING);
}
/* Release uthread table control block and kernel stacks segment. */
pm_release(&U.U_uthread_cb);
if (p->p_kstackseg != NULLSEGVAL)
{
pm_release(&U.U_cancel_cb);
vms_delete(SRTOSID(p->p_kstackseg));
p->p_kstackseg = NULLSEGVAL;
uthr0.ut_kstack = (char *)&__ublock;
}
/* Reallocate uthread table control block */
(void)pm_init( &U.U_uthread_cb, /* zone */
(char *)&__ublock.ub_uthr, /* start */
(char *)&__ublock + sizeof(struct ublock),/* end */
sizeof(struct uthread), /* size */
(char *)&uthr0.ut_link - (char *)&uthr0, /* link */
UTHREAD_ZONE_CLASS, /* class */
p-proc, /* occur */
PM_FIFO); /* flags */
/*
* Clean up the zombies.
*
* Note: While all other state transitions are protected by the
* proc_int_lock, TSZOMB->TSNONE is protected by the proc_tbl_lock
* for the scheduler.
*/
if (tzomb != NULL) {
struct thread *thr;
simple_lock(&proc_tbl_lock);
th = tzomb;
do {
thr = th;
th = th->t_nextthread;
freethread(thr);
} while (th != tzomb);
simple_unlock(&proc_tbl_lock);
}
if (audit_flag)
svcrc = audit_svcstart("PROC_Execute", &svcnum, 3,
U.U_cred->cr_uid, U.U_cred->cr_gid, U.U_cred->cr_epriv);
if(svcrc){
if(fname){
char *ptr;
int len;
if((ptr = malloc(MAXPATHLEN)) == NULL){
*errorp = ENOMEM;
goto bad;
}
if(copyinstr(fname, ptr, MAXPATHLEN, &len)){
*errorp = EFAULT;
goto bad;
}
audit_svcbcopy(ptr, len);
free(ptr);
}
}
/* initialize accounting field - executed by superuser */
if (!privcheck(SET_PROC_RAC))
U.U_acflag |= ASU;
/* initialize exec argument structure */
xp = &xargs;
xp->fname = (char *)fname;
xp->argp = (char **)argp;
xp->envp = (char **)envp;
xp->libpath = NULL;
xp->buf = NULL;
xp->ebuf = NULL;
xp->indir = 0;
xp->loaderror = 0;
/* come back to try to exec /etc/execerror after execload fails*/
eagain:
/*
* Open the exec file. The fp_xopen() service returns u.u_error.
* If tracing, we require read permission as well as execute.
* The basename of the resolved path is returned in xp->cfname.
*/
oflags = O_EXEC | ((p->p_flag & STRC) ? O_RDONLY : O_NONE);
*errorp = 0;
if ((*errorp = fp_xopen(xp->fname, xp->loaderror?FP_SYS:FP_USR, oflags,
xp->cfname, sizeof(xp->cfname), &fp)) != 0) {
fp = NULL;
goto bad;
}
/* save important attributes of the original exec file */
if (fp_fstat(fp, &stst, sizeof(struct stat), FP_SYS) == -1) {
*errorp = EACCES;
goto bad;
}
/* not a regular file - its a directory or other bad thing*/
if (!S_ISREG(stst.st_mode)) {
*errorp = EACCES; /* not a regular file - it is */
goto bad; /* a dir or other bad thing. */
}
/* branch back here after opening an indirect file */
again:
/*
* If privileged, the access checking in open will
* have succeeded, but we still don't want to run
* programs without execute permission for at least someone.
*/
if ((*errorp = fp_access(fp, IEXEC)) != 0)
goto bad;
/*
* Read in first few bytes of file for segment sizes, magic number.
* Also an ASCII line beginning with #! is the file name of a
* ``shell'' (or other interpreter) and an argument may be prepended
* to the argument list if given there.
*/
hdr.u_exshell[0] = '\0'; /* for zero length files */
rc = fp_read(fp, (char *)&hdr, sizeof(hdr), 0, UIO_SYSSPACE, &bcount);
if (rc) {
*errorp = rc;
goto bad;
}
if ((bcount < sizeof(hdr))
&& (hdr.u_exshell[0] != '#')) { /* not enough read? */
*errorp = ENOEXEC; /* invalid load module format */
goto bad;
}
/*
* clean this up for machine dependencies and add a hook for
* dealing with old load module formats and other machine
* architectures.
*/
switch ((int)hdr.u_xcoffhdr.filehdr.f_magic) {
register char *cp; /* pointer for scanning command name */
register char *indname; /* indirect file name pointer */
register char *indbase; /* indirect file basename pointer */
char *tail ; /* end of interpretor line */
case XCOFF_MAGIC: /* XCOFF magic for this machine */
if (hdr.u_xcoffhdr.aouthdr.o_tsize == 0) {
*errorp = ENOEXEC;
goto bad;
}
break;
default:
if (hdr.u_exshell[0] != '#' || /* not an indirect file? */
hdr.u_exshell[1] != '!' ||
xp->indir) { /* already indirect? */
/*
* ADD CODE TO CALL A PLATFORM-DEPENDENT MODULE FOR
* HANDLING OLD BINARIES, OTHER ARCHITECTURES, ETC.
*/
*errorp = ENOEXEC;
goto bad;
}
/* find end of line; turning tabs into blanks */
tail = &hdr.u_exshell[2]; /* skip "#!" */
while (tail < &hdr.u_exshell[INTERPRET]) {
if (*tail == '\t')
*tail = ' ';
else if (*tail == '\n') {
*tail = '\0';
break;
}
tail++;
}
if (*tail++ != '\0') { /* newline not found? */
*errorp = ENOEXEC;
goto bad;
}
/* scan for "shell" file name */
cp = &hdr.u_exshell[2];
while (*cp == ' ') /* skip leading white space */
cp++;
indname = indbase = cp; /* start of indirect file name */
while (*cp && *cp != ' ') { /* find end of file name */
if ( *cp == '/' )
indbase = cp +1;
cp++;
}
xp->cfarg[0] = '\0'; /* in case there's no arg */
if (*cp) { /* not at end of line? */
*cp++ = '\0'; /* mark end of file name string */
while (*cp == ' ')
cp++; /* skip white space */
if (*cp) /* is there an arg? */
bcopy((caddr_t)cp, (caddr_t)xp->cfarg, tail-cp);
}
if(audit_flag)
{
fp_indirect = fp ;
bcopy((caddr_t)xp->cfname,(caddr_t)auditName,MAXCOMLEN);
}
else
(void)fp_close(fp);
xp->indir = 1; /* set file indirection flag */
/*
* Open the interpreter file. Saving its basename in
* xp->cfname seems strange, but that's the way it has
* always worked.
*/
if ((*errorp = fp_xopen(indname, FP_SYS, oflags,
NULL, 0, &fp)) != 0){
fp=NULL;
goto bad; /* open failed */
}
if (fp_fstat(fp, &stst, sizeof(stst), FP_SYS) == -1) {
*errorp = EACCES;
goto bad;
}
if (!S_ISREG(stst.st_mode)){
*errorp = EACCES; /* not a regular file - it is */
goto bad; /* a dir or other bad thing. */
}
copyname(xp->cfname, indbase, sizeof(xp->cfname));
goto again;
}
/*
* Copy arguments from the user memory into kernel memory.
* If there was an error during load, then the arguments and
* the env. variables would be in kernel memory and we don't
* have to do copyargs_in().
*
* Pass the address of the libpath_ptr as an argument to copyargs_in.
* copyargs_in copies the arguments and environment variables to
* kernel memory. The LIBPATH environment variable is not copied
* into the buffer. Instead, copyargs_in returns the position of
* the LIBPATH variable in envp[] array. 'libpath_ptr' can be used
* to retrieve the LIBPATH after the privileges are determined with
* the exec_priv() function. This needs to be done before exec_priv,
* because the latter may alter the privilege of the current process
* and this function may fail. copyargs_in() also returns the total
* length of the environment strings in the buffer. This is needed to
* retrieve the env. strings from the buffer in case of an error
* during load. 'env_len' is used to save the length of env. strings.
*/
if (!xp->loaderror){
/* allocate a pageable buffer to hold the strings */
if ((xp->buf = xmalloc(NCARGS, PGSHIFT, kernel_heap)) == NULL) {
*errorp = ENOMEM;
goto bad;
}
if (*errorp = copyargs_in(xp, &libpath_ptr, &env_len))
goto bad;
/* Save the total length of arguments so that the env.
* strings can be retrieved in case of an error during load.
*/
arg_len = xp->nc - env_len;
}
/*
* Let the new process acquire/inherit privileges. There are 3
* return values:
*
* 1 the process has had it's privileges amplified
* in this case don't let the user specify an alternate
* libpath. we also need to remove LIBPATH from the
* user's environment, to prevent subsequent exec's
* from using LIBPATH. We still need to copy in LIBPATH
* so that a loader domain name can be retrieved if
* necessary. xp->libpath is set to NULL.
*
* 0 the process has not had it's privileges amplified
* (normal case). In this case, we have to keep the
* LIBPATH. This is done by retrieving the LIBPATH
* from xp->envp with the help of libpath_ptr (returned
* by copyargs_in). xp->envp+libpath_ptr points to the
* LIBPATH. The LIBPATH is then appended to xp->buf.
*
* -1 the process is operating on the Trusted Path and
* the file to be exec'd does not have the S_ITP bit
* bit set in the mode (i.e. the file is not "trusted").
* In this case the exec fails...
*
* After this there is no turning back.
*/
rc = exec_priv(fp, &stst);
if (rc == -1) {
(void)fp_close(fp); /* close load module file */
fp = NULL;
*errorp = ENOTRUST;
goto kill;
}
else if (rc == 0 || rc == 1) {
/*
* In case of error during load, LIBPATH already exists
* in the buffer and we don't have to copy it in from
* the user memory.
* If LIBPATH variable exists in the environment, then
* copy into the buffer. copyargs_in sets xp->libpath
* if it exists in the envp[] array. If the xp->libpath
* is not set, then LIBPATH did not exist at all and there
* is nothing to copy. If the xp->libpath is set, then
* xp->env+libpath_ptr points to the LIBPATH.
*
* In the case where rc == 1, we still copy the LIBPATH
* from user to kernel space. This is done so that any
* loader domain specified can be passed to the process.
* However, the variables which describe the environment
* are not updated to reflect LIBPATH. Therefore, it
* will not be copied out(from kernel to user).
*/
if (xp->libpath && !xp->loaderror) {
register caddr_t ap; /* Pointer to LIBPATH */
register char *cp; /* Pointer to end of buffer */
uint len; /* Length of LIBPATH */
/* Retrieve the LIBPATH from user memory */
ap = (caddr_t) fuword((caddr_t)(xp->envp+libpath_ptr));
cp = xp->buf+xp->nc; /* Append the LIBPATH to the buffer */
if (*errorp = copyinstr(ap,cp,(unsigned)NCARGS-xp->nc, &len))
goto kill;
if (rc == 0) { /* case where privileges are not amplified */
xp->ne++; /* Increment no. of env strings */
xp->nc += len; /* Increment no. of chars in buffer */
xp->libpath = cp+8; /* Set the LIBPATH */
ldomain_path = cp+8; /* Set loader domain path */
env_len += len; /* Increment total length of env strgs*/
}
else { /* case where privileges are amplified */
ldomain_path = cp+8; /* Set loader domain path */
xp->libpath = NULL;
}
}
}
/* round number of characters up to an even number of words */
xp->nc = (xp->nc + NBPW-1) & ~(NBPW-1);
/* get rid of old text etc - we are now committed to execing
* N.B. if more than one loader is supported, this call must be
* based on the loader type of the old module, not the new one. */
ld_usecount(-1);
/* zero most of the data area - this is a machine dependent operation
* since the optional strategy depends on details of the memory
* management system.
* The simple lock is used to protect the adspace manipulations
* and prevent getproc() from xmattach'ing the process private segment
* during VMM operations.
*/
simple_lock(&p->p_lock);
rc = vm_cleardata(&U.U_ufd[U.U_maxofile + 1]);
simple_unlock(&p->p_lock);
if (rc)
goto kill;
/* identify user segments that can be deleted */
freeuspace(NULL);
/* if process was profiling, free profiling resources */
if (U.U_prof.pr_scale)
prof_off();
/* if memory was locked, unpin locked text or data */
if (U.U_lock & (PROCLOCK|TXTLOCK|DATLOCK))
plock(UNLOCK);
*errorp = 0;
/* release mmap resources -- must be done before shmex() */
if (U.U_map)
{
vm_map_deallocate(U.U_map);
U.U_map = NULL;
}
shmex(); /* release shared memory segments */
sigexec(); /* update signal state */
fs_exec(); /* close files that were requested */
/* If user specified a compile-time max stack size, give it */
/* to him with a setrlimit() call. If setrlimit() fails for whatever */
/* reason, abort the exec process. Need to convert the desired */
/* stack size to an absolute address for setrlimit() to handle */
if(hdr.u_xcoffhdr.aouthdr.o_maxstack)
{
simple_lock(&U.U_handy_lock);
alim.rlim_max = u.u_rlimit[RLIMIT_STACK].rlim_max;
alim.rlim_cur = hdr.u_xcoffhdr.aouthdr.o_maxstack;
rc = xsetrlimit(RLIMIT_STACK, &alim);
simple_unlock(&U.U_handy_lock);
if (rc == -1)
goto kill;
}
/* adjust data limit from o_maxdata similiarly */
if (hdr.u_xcoffhdr.aouthdr.o_maxdata)
{
simple_lock(&U.U_handy_lock);
alim.rlim_max = u.u_rlimit[RLIMIT_DATA].rlim_max;
alim.rlim_cur = BDATAORG - PRIVORG +
(( hdr.u_xcoffhdr.aouthdr.o_maxdata >
MAX_LARGE_DATA_SET_MODEL ) ?
MAX_LARGE_DATA_SET_MODEL :
hdr.u_xcoffhdr.aouthdr.o_maxdata) ;
rc = xsetrlimit(RLIMIT_DATA, &alim);
simple_unlock(&U.U_handy_lock);
if (rc == -1)
goto kill;
}
/* Establish process private segment early paging space */
/* allocation state. Shrink heap so paging space requirements */
/* will not be overestimated. */
if (p->p_flag & SPSEARLYALLOC)
{
U.U_dsize = 0;
sbreak(PRIVORG + 1);
if (*errorp = vms_psearlyalloc(SRTOSID(p->p_adspace)))
goto kill;
}
else
vms_pslatealloc(SRTOSID(p->p_adspace));
if (!(xp->ep = ld_execload(fp,xp->libpath,ldomain_path)))
{ /* loader failed? */
register char **arg; /* pointer to error messages */
int errmsg_len = 0; /* Total length of error strings */
int errmsg_ctr = 0; /* Total no. of error messages */
char *cp; /* Buffer for argument & env. strings */
int len;
int offset;
(void)fp_close(fp); /* close load module file */
fp = NULL;
if (xp->loaderror|(p->p_flag&STRC) )
goto kill; /* kill user process */
xp->loaderror = 1;
xp->indir = 0;
xp->fname = "/usr/sbin/execerror";
xp->na = 0;
xp->nc = 0;
xp->libpath = NULL;
ldomain_path = NULL;
/* Allocate a buffer for the error messages to be
* passed to ld_emessdump().
*/
if ((errmsg_buf = xmalloc(NCARGS, PGSHIFT, kernel_heap))== NULL)
goto kill;
/* emessdump fills buf with a vector of char pointers followed
* by the strings they point to. The vector is NULL terminated,
* just like any other parameter list */
ld_emessdump(errmsg_buf,NCARGS, SYS_ADSPACE);
arg = (void *)errmsg_buf;
/* Get the number of error messages and the length */
while(*arg != NULL)
{
errmsg_len += strlen(*arg)+1;
errmsg_ctr++;
arg++;
}
/* Allocate a buffer for the error messages & env. strings to be
* passed to execerror.
*/
if ((xp->ebuf = xmalloc(NCARGS, PGSHIFT, kernel_heap)) == NULL)
goto kill;
/* Put the arguments and the env.strings in xp->ebuf */
cp = xp->ebuf;
if (copystr("execerror", cp, (unsigned)NCARGS, &len))
goto kill;
cp += len;
xp->nc += len;
xp->na++;
/* Copy the filename from xp->buf */
if (copystr(xp->buf, cp, (unsigned)NCARGS-xp->nc, &len))
goto kill;
cp += len;
xp->nc += len;
xp->na++;
/* Copy the error messages from errmsg_buf */
if (errmsg_len > NCARGS-xp->nc){
*errorp = ENOMEM;
goto kill;
}
offset = (errmsg_ctr+1)*sizeof(void *);
bcopy(errmsg_buf+offset, cp, errmsg_len);
xp->nc += errmsg_len;
xp->na += errmsg_ctr;
TRCGENT(0,HKWD_SYSC_EXECVE,1,xp->nc,xp->ebuf);
/* Compute remaining character count */
if (env_len > (NCARGS-xp->nc)){
*errorp = ENOMEM;
goto kill;
}
/* Append the env. variables in xp->ebuf */
cp = xp->ebuf+xp->nc;
bcopy(xp->buf+arg_len, cp, env_len);
xp->nc += env_len;
/* Free the buffer containing the error messages */
xmfree(errmsg_buf, kernel_heap);
errmsg_buf = NULL;
goto eagain;
}
if(fp_indirect)
{
lockt = lockl(&kernel_lock, LOCK_SHORT);
bcopy((caddr_t) auditName, (caddr_t)U.U_comm, MAXCOMLEN);
aud_vn_exec(fp_indirect->f_vnode);
(void)fp_close(fp_indirect);
fp_indirect = NULL;
if (lockt != LOCK_NEST) unlockl(&kernel_lock);
}
/* save file basename for accounting */
bcopy((caddr_t)xp->cfname, (caddr_t)U.U_comm, MAXCOMLEN);
if(audit_flag) {
lockt = lockl(&kernel_lock, LOCK_SHORT);
aud_vn_exec(fp->f_vnode);
if (lockt != LOCK_NEST) unlockl(&kernel_lock);
}
(void)fp_close(fp); /* close load module file */
fp = NULL;
/* set up machine-dependent user stack context for main() */
if (*errorp = setregs(xp))
goto kill; /* kill user process */
/* copy arglist back to top of user stack. */
if (*errorp = copyargs_out(xp))
goto kill; /* kill user process */
xmfree(xp->buf, kernel_heap); /* free the argument buffer */
xp->buf = NULL;
/* In case of an error during load, free xp->ebuf */
if (xp->loaderror){
xmfree(xp->ebuf, kernel_heap); /* free the argument buffer */
xp->ebuf = NULL;
}
/* Execve has succeeded. It is now safe to reset errnop and userdata */
errnop = &errno;
t->t_uthreadp->ut_errnopp = &errnop;
t->t_userdata = 0;
sysinfo.sysexec++; /* count number of exec's */
cpuinfo[CPUID].sysexec++;
U.U_acflag &= ~AFORK; /* reset the accounting flag */
p->p_size = btoc(U.U_dsize + U.U_tsize);
p->p_flag |= SEXECED; /* process has exec'd */
if (p->p_flag & STRC)
{
/* if tracing, then signal curthread to stop it */
pidsig(p->p_pid, SIGTRAP);
/* private copy text, shlib */
assert(ld_ptrace(p->p_pid) == 0);
/* if debugging across exec's, set stopped in exec flag */
if (p->p_flag & SMPTRACE)
fetch_and_or((atomic_p)&p->p_atomic, SEWTED);
}
BUGLPR(excdbg, BUGACT,
("exec: completed sucessfully\n"));
/*
* Commit audit record
*/
if(svcrc){
/*
* Must do an immediate commit now since state is
* not saved
* u.u_error should be zero, but is EINVAL
*/
*errorp = 0;
audit_svcfinis();
auditscall();
}
/*
* We need to release the special fork stack before leaving.
* However we know for a fact that we are a monothreaded process,
* therefore we don't have to protect the resetting of SFORKSTACK.
* we don't have to wake up anybody and we don't have to actually
* give up the special fork stack, we can stay on it until
* the end of the system call.
*/
p->p_flag &= ~(SFORKSTACK|SEXECING);
/*
* The execve() system call is special, because it does not
* resume the state that had been saved on the user stack
* on entry. This stack no longer exists. We could fake
* out the contents of the new stack to look as if it had
* done a system call with the appropriate resume state saved,
* but it seems simpler just to pass the new user context to
* a special system call handler function, execexit().
*/
execexit(xp->ucp);
/* NEVER RETURNS */
/*
* errors that occur after the "point of no return" result in
* killing the new partially-constructed process image.
*/
kill:
xp->loaderror = 1; /* force kill */
/*
* if any error occurred during exec, free resources and return
*/
bad:
/*
* Commit audit record
* Don't wait for svc handler (low.s)
*/
if(svcrc){
audit_svcfinis();
auditscall();
}
if (xp->buf)
xmfree(xp->buf, kernel_heap);
if (xp->ebuf)
xmfree(xp->ebuf, kernel_heap);
if (errmsg_buf)
xmfree(errmsg_buf, kernel_heap);
if (fp)
(void)fp_close(fp);
if (fp_indirect)
(void)fp_close(fp_indirect);
if (xp->loaderror){
BUGLPR(excdbg, BUGNFO,
("exec: process image killed.\n"));
kthread_kill(-1, SIGKILL);
}
BUGLPR(excdbg, BUGNFO,
("exec: u.u_error = %d.\n", (int) *errorp));
/*
* We know for a fact that we are a monothreaded process,
* therefore we don't have to protect the resetting of STERM,
* and we don't have to wake up anybody.
*/
p->p_flag &= ~(SEXECING|SFORKSTACK);
return(-1);
}
/*
* NAME: copyargs_in
*
* FUNCTION: Copy arguments into kernel memory.
*
* Allocates a kernel buffer to hold the user argument strings,
* then copies the data into kernel memory.
*
* RETURNS:
* zero if successful; errno value otherwise.
* The position of LIBPATH in the envp[] array is returned
* in libpath_ptr.
* The total length of environment strings is returned in
* env_lenp.
*/
static int
copyargs_in(register struct xargs *xp, int *libpath_ptr, int *env_lenp)
/* xp pointer to exec argument structure */
/* libpath_ptr position of LIBPATH in the envp[] array */
/* env_lenp total length of the environment strings */
{
register char *cp = xp->buf; /* next character in buffer */
register int cc = NCARGS; /* remaining character count */
register char *ap; /* argument pointer */
register char **argp = xp->argp;
register char **envp = xp->envp;
register int error;
uint len; /* length of string copied */
xp->na = xp->nc = xp->ne = 0;
U.U_procp->p_flag &= ~SPSEARLYALLOC;
if (argp == NULL) {
TRCGENT(0,HKWD_SYSC_EXECVE,1,strlen(xp->fname),xp->fname);
return(0);
}
if (xp->indir) {
ap = xp->cfname; /* indirect file name */
argp++; /* ignore argv[0] */
xp->na++;
if (error = copystr(ap, cp, (unsigned)cc, &len))
return(error);
cp += len;
cc -= len;
if (xp->cfarg[0]) {
ap = xp->cfarg;/* indirect arg is next */
xp->na++;
if (error = copystr(ap, cp, (unsigned)cc, &len))
return(error);
cp += len;
cc -= len;
}
ap = xp->fname; /* exec'ed file name next */
xp->na++;
if (error = copyinstr(ap, cp, (unsigned)cc, &len))
return(error);
cp += len;
cc -= len;
}
for (;;) {
if ((ap = (char *)fuword(argp)) == (char *)-1)
return(EFAULT);
else if (ap == NULL)
break;
argp++; /* next arg pointer */
xp->na++;
if (error = copyinstr(ap, cp, (unsigned)cc, &len))
return(error);
cp += len;
cc -= len;
}
TRCGENT(0,HKWD_SYSC_EXECVE,1,cp-xp->buf,xp->buf);
if (envp == NULL) {
xp->nc = NCARGS - cc;
return(0);
}
for(;;) {
if ((ap = (char *)fuword(envp)) == (char *)-1)
return(EFAULT);
else if (ap == NULL)
break;
envp++; /* next env pointer */
xp->ne++;
if (error = copyinstr(ap, cp, (unsigned)cc, &len))
return(error);
/* check for meaningful environment variable */
switch (*cp) {
case 'L': /* check for LIBPATH environment variable */
if (memcmp(cp,"LIBPATH=",8) == 0) {
len = 0; /* don't copy in */
xp->ne--;
/* Save the position of LIBPATH in envp */
*libpath_ptr = xp->ne;
/* Set xp->libpath as an indication that
* LIBPATH exists in envp[]
*/
xp->libpath = cp+8;
}
break;
case 'P': /* check for PSALLOC environment variable */
if (memcmp(cp,"PSALLOC=early",14) == 0)
U.U_procp->p_flag |= SPSEARLYALLOC;
break;
default:
break;
}
cp += len;
cc -= len;
*env_lenp += len;
}
xp->nc = NCARGS - cc; /* Store number of characters in the buffer */
return(0);
}
/*
* NAME: copyargs_out
*
* FUNCTION: Copy arguments out to top of user stack.
*
* Allocates a kernel buffer to hold the user argument strings,
* then copies the data into kernel memory.
*
* RETURNS:
* zero if successful; errno value otherwise.
*/
static int
copyargs_out(register struct xargs *xp)
/* xp pointer to exec argument structure */
{
register caddr_t ap = xp->ap; /* argument area pointer */
register char *cp; /* string buffer pointer */
register char *buf; /* string buffer pointer */
register caddr_t usp; /* user stack string pointer */
register int na = xp->na; /* number of arg characters */
register int ne = xp->ne; /* number of env characters */
caddr_t strp; /* user stack string area */
/* If there was an error during load, then the
* xp->ebuf points to the string buffer; else
* xp->buf contains the string buffer.
*/
if (xp->loaderror)
cp = buf = xp->ebuf;
else
cp = buf = xp->buf;
/* compute and remember address of string area */
usp = strp = ap + XA_ARGSIZE(xp) + XA_ENVSIZE(xp);
/* store the argument list pointers in the argument area */
while (na--) {
if (suword(ap, usp))
return(EFAULT);
while(usp++, *cp++) /* scan past end of string */
;
ap += NBPW;
}
/* store a null pointer at the end of the argument list */
if (suword(ap, NULL))
return(EFAULT);
ap += NBPW;
/* store the environment list pointers in the argument area */
while (ne--) {
if (suword(ap, usp))
return(EFAULT);
while(usp++, *cp++) /* scan past end of string */
;
ap += NBPW;
}
/* store a null pointer at the end of the environment list */
if (suword(ap, NULL))
return(EFAULT);
ap += NBPW;
/* copy the whole string area in one move */
if (copyout(buf, strp, xp->nc))
return(EFAULT);
return(0);
}