Files
Arquivotheca.Solaris-2.5/uts/common/os/exec.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

736 lines
16 KiB
C
Executable File

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
#ident "@(#)exec.c 1.79 95/03/16 SMI" /* from S5R4 1.33.2.1 */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/user.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/vfs.h>
#include <sys/vnode.h>
#include <sys/mman.h>
#include <sys/acct.h>
#include <sys/cpuvar.h>
#include <sys/proc.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/pathname.h>
#include <sys/vm.h>
#include <sys/lock.h>
#include <sys/vtrace.h>
#include <sys/exec.h>
#include <sys/exechdr.h>
#include <sys/kmem.h>
#include <sys/prsystm.h>
#include <sys/modctl.h>
#include <sys/vmparam.h>
#include <vm/hat.h>
#include <vm/anon.h>
#include <vm/as.h>
#include <vm/seg.h>
#include <vm/seg_vn.h>
int nullmagic = 0; /* null magic number */
static int execsetid(struct vnode *, struct vattr *, uid_t *, uid_t *);
static int hold_execsw(struct execsw *);
int auxv_hwcap = 0; /* auxv AT_SUN_HWCAP value; determined on the fly */
int kauxv_hwcap = 0; /* analogous kernel version of the same flag */
#ifdef i386
extern void ldt_free(proc_t *pp);
extern faultcode_t forcefault(caddr_t addr, int len);
#endif /* i386 */
/*
* exec system calls, without and with environments.
*/
int
exec(uap, rvp)
struct execa *uap;
rval_t *rvp;
{
uap->envp = NULL;
return (exece(uap, rvp));
}
/* ARGSUSED */
int
exece(uap, rvp)
struct execa *uap;
rval_t *rvp;
{
vnode_t *vp = NULL;
register proc_t *p = ttoproc(curthread);
klwp_id_t lwp = ttolwp(curthread);
struct user *up = PTOU(p);
long execsz; /* temporary count of exec size */
int i, error = 0;
char exec_file[PSCOMSIZ];
struct pathname pn;
struct uarg args;
if (curthread == p->p_aslwptp) {
/*
* The aslwp cannot call exec(). Return error.
*/
return (EACCES);
}
/*
* Leave only the current lwp and force the other lwps
* to exit. Since exitlwps() waits until all other lwps are dead, if
* the calling process has an aslwp, all pending signals from it will
* be transferred to this process before continuing past this call.
*/
exitlwps(p, 0);
ASSERT((p->p_flag & ASLWP) == 0);
ASSERT(p->p_aslwptp == NULL);
/*
* Delete the dot4 timers.
*/
if (p->p_itimer != NULL)
timer_exit();
CPU_STAT_ADD_K(cpu_sysinfo.sysexec, 1);
execsz = btoc(SINCR) + btoc(SSIZE) + btoc(NCARGS-1);
/*
* Lookup path name and remember last component for later.
*/
if (error = pn_get(uap->fname, UIO_USERSPACE, &pn))
return (error);
if (error = lookuppn(&pn, FOLLOW, NULLVPP, &vp)) {
pn_free(&pn);
return (error);
}
strncpy(exec_file, pn.pn_path, PSCOMSIZ);
struct_zero((caddr_t)&args, sizeof (args));
pn_free(&pn);
/*
* Inform /proc that an exec() has started.
*/
prexecstart();
if (error = gexec(vp, uap, &args, (struct intpdata *)NULL,
0, &execsz, (caddr_t)exec_file, p->p_cred))
goto done;
/*
* Free floating point registers (sun4u only)
*/
if (lwp)
lwp_freeregs(lwp);
/*
* Free device context
*/
if (curthread->t_ctx)
freectx(curthread);
up->u_execsz = execsz; /* dependent portion should have checked */
/*
* Remember file name for accounting.
*/
up->u_acflag &= ~AFORK;
bcopy((caddr_t)exec_file, (caddr_t)up->u_comm, PSCOMSIZ);
/*
* Reset stack state to the user stack, clear set of signals
* caught on the signal stack, and reset list of signals that
* restart system calls; the new program's environment should
* not be affected by detritus from the old program. Any pending
* signals remain held, so don't clear p_hold and p_sig.
*/
mutex_enter(&p->p_lock);
lwp->lwp_oldcontext = 0;
sigemptyset(&up->u_signodefer);
sigemptyset(&up->u_sigonstack);
sigemptyset(&up->u_sigresethand);
lwp->lwp_sigaltstack.ss_sp = 0;
lwp->lwp_sigaltstack.ss_size = 0;
lwp->lwp_sigaltstack.ss_flags = SS_DISABLE;
/*
* If the action was to catch the signal, then the action
* must be reset to SIG_DFL.
*/
for (i = 1; i < NSIG; i++) {
if (up->u_signal[i - 1] != SIG_DFL &&
up->u_signal[i - 1] != SIG_IGN) {
up->u_signal[i - 1] = SIG_DFL;
sigemptyset(&up->u_sigmask[i - 1]);
if (sigismember(&ignoredefault, i)) {
sigdelq(p, NULL, i);
sigdelq(p, p->p_tlist, i);
}
}
}
sigorset(&p->p_ignore, &ignoredefault);
sigdiffset(&p->p_siginfo, &ignoredefault);
sigdiffset(&p->p_sig, &ignoredefault);
sigdiffset(&p->p_tlist->t_sig, &ignoredefault);
p->p_flag &= ~(SNOWAIT|SJCTL|SWAITSIG);
p->p_flag |= SEXECED;
/*
* Reset lwp id and lpw count to default.
* This is a single-threaded process now.
*/
curthread->t_tid = 1;
p->p_lwptotal = 1;
p->p_lwpblocked = -1;
/*
* Delete the dot4 signal queues.
*/
if (p->p_sigqhdr != NULL)
sigqfree(p);
mutex_exit(&p->p_lock);
#ifdef i386
/* If the process uses a private LDT then change it to default */
if (p->p_ldt)
ldt_free(p);
#endif
/*
* Close all close-on-exec files. Don't worry about locking u_lock
* when looking at u_nofiles because there should only be one lwp
* at this point.
*/
close_exec(p);
#ifdef TRACE
trace_process_name((u_long) (p->p_pid), u.u_psargs);
#endif /* TRACE */
TRACE_3(TR_FAC_PROC, TR_PROC_EXEC, "proc_exec:pid %d as %x name %s",
p->p_pid, p->p_as, up->u_psargs);
setregs();
done:
VN_RELE(vp);
/*
* Inform /proc that the exec() has finished.
*/
prexecend();
return (error);
}
/*
* Perform generic exec duties and switchout to object-file specific
* handler.
*/
int
gexec(vp, uap, args, idatap, level, execsz, exec_file, cred)
struct vnode *vp;
struct execa *uap;
struct uarg *args;
struct intpdata *idatap;
int level;
long *execsz;
caddr_t exec_file;
struct cred *cred;
{
register proc_t *pp = ttoproc(curthread);
register struct execsw *eswp;
int error = 0;
int resid;
uid_t uid, gid;
struct vattr vattr;
char magbuf[4], tbuf[2];
short magic, tmagic;
int setid;
struct cred *newcred = NULL;
if ((error = execpermissions(vp, &vattr, args)) != 0)
goto bad;
/* need to open vnode for stateful file systems like rfs */
if ((error = VOP_OPEN(&vp, FREAD, CRED())) != 0)
goto bad;
/*
* Note: to support binary compatibility with SunOS a.out
* executables, we read in the first four bytes, as the
* magic number is in bytes 2-3.
*/
if (error = vn_rdwr(UIO_READ, vp, magbuf, sizeof (magbuf),
(off_t)0, UIO_SYSSPACE, 0, (long)0, CRED(), &resid))
goto bad;
if (resid != 0)
goto bad;
magic = (short)getexmag(magbuf);
/*
* If this is a SunOS a.out, fix up the magic number.
*/
tbuf[0] = magbuf[2];
tbuf[1] = magbuf[3];
tmagic = (short)getexmag(tbuf);
if (tmagic == NMAGIC || tmagic == ZMAGIC || tmagic == OMAGIC)
magic = tmagic;
if ((eswp = findexectype(magic)) == NULL)
goto bad;
if (level == 0 && execsetid(vp, &vattr, &uid, &gid)) {
/*
* if the suid/euid are not suser, check if
* cred will gain any new uid/gid from exec;
* if new id's, set a bit in p_flag for core()
*/
if (cred->cr_suid && cred->cr_uid) {
if ((((vattr.va_mode & VSUID) &&
uid != cred->cr_suid && uid != cred->cr_uid)) ||
((vattr.va_mode & VSGID) &&
gid != cred->cr_sgid && !groupmember(gid, cred))) {
mutex_enter(&pp->p_lock);
pp->p_flag |= NOCD;
mutex_exit(&pp->p_lock);
}
}
newcred = crdup(cred);
newcred->cr_uid = uid;
newcred->cr_gid = gid;
newcred->cr_suid = uid;
newcred->cr_sgid = gid;
cred = newcred;
}
/*
* execsetid() told us whether or not we had to change the
* credentials of the process. It did not tell us whether
* the executable is marked setid. We determine that here.
*/
setid = (vp->v_vfsp->vfs_flag & VFS_NOSUID) == 0 &&
(vattr.va_mode & (VSUID|VSGID)) != 0;
error = (*eswp->exec_func)(vp, uap, args, idatap, level,
execsz, setid, exec_file, cred);
rw_exit(eswp->exec_lock);
if (error != 0) {
if (newcred != NULL)
crfree(newcred);
goto bad;
}
if (level == 0) {
if (newcred != NULL) {
/*
* Free the old credentials, and set the new ones.
* Do this for both the process and the (single) thread.
*/
crfree(pp->p_cred);
pp->p_cred = cred; /* cred already held for proc */
crhold(cred); /* hold new cred for thread */
crfree(curthread->t_cred);
curthread->t_cred = cred;
}
if (setid && (pp->p_flag & STRC) == 0) {
/*
* If process is traced via /proc, arrange to
* invalidate the associated /proc vnode.
*/
if (pp->p_plist || (pp->p_flag & SPROCTR))
args->traceinval = 1;
}
if (pp->p_flag & STRC)
psignal(pp, SIGTRAP);
if (args->traceinval)
prinvalidate(&pp->p_user);
}
return (0);
bad:
if (error == 0)
error = ENOEXEC;
return (error);
}
extern char *execswnames[];
struct execsw *
allocate_execsw(char *name, short magic)
{
register int i;
register char *ename;
register short *magicp;
mutex_enter(&execsw_lock);
for (i = 0; i < nexectype; i++) {
if (execswnames[i] == NULL) {
ename = kmem_alloc(strlen(name) + 1, KM_SLEEP);
strcpy(ename, name);
execswnames[i] = ename;
/*
* Set the magic number last so that we
* don't need to hold the execsw_lock in
* findexectype().
*/
magicp = (short *)kmem_alloc(sizeof (short), KM_SLEEP);
*magicp = magic;
execsw[i].exec_magic = magicp;
mutex_exit(&execsw_lock);
return (&execsw[i]);
}
}
mutex_exit(&execsw_lock);
return (NULL);
}
struct execsw *
findexecsw(short magic)
{
register struct execsw *eswp;
for (eswp = execsw; eswp < &execsw[nexectype]; eswp++) {
if (magic && magic == *eswp->exec_magic)
return (eswp);
}
return (NULL);
}
/*
* Find the execsw[] index for a given magic number. If no execsw[] entry
* is found, try to autoload a module for this magic number.
*/
struct execsw *
findexectype(short magic)
{
register struct execsw *eswp;
for (eswp = execsw; eswp < &execsw[nexectype]; eswp++) {
if (magic && magic == *eswp->exec_magic) {
if (hold_execsw(eswp) != 0)
return (NULL);
return (eswp);
}
}
return (NULL); /* couldn't find the type */
}
static int
hold_execsw(struct execsw *eswp)
{
register char *name;
rw_enter(eswp->exec_lock, RW_READER);
while (!LOADED_EXEC(eswp)) {
rw_exit(eswp->exec_lock);
name = execswnames[eswp-execsw];
ASSERT(name);
if (modload("exec", name) == -1)
return (-1);
rw_enter(eswp->exec_lock, RW_READER);
}
return (0);
}
static int
execsetid(vp, vattrp, uidp, gidp)
struct vnode *vp;
struct vattr *vattrp;
uid_t *uidp;
uid_t *gidp;
{
register proc_t *pp = ttoproc(curthread);
uid_t uid, gid;
/*
* Remember credentials.
*/
uid = pp->p_cred->cr_uid;
gid = pp->p_cred->cr_gid;
if ((vp->v_vfsp->vfs_flag & VFS_NOSUID) == 0) {
if (vattrp->va_mode & VSUID)
uid = vattrp->va_uid;
if (vattrp->va_mode & VSGID)
gid = vattrp->va_gid;
}
/*
* Set setuid/setgid protections if no ptrace() compatibility.
* For the super-user, honor setuid/setgid even in
* the presence of ptrace() compatibility.
*/
if (((pp->p_flag & STRC) == 0 || pp->p_cred->cr_uid == 0) &&
(pp->p_cred->cr_uid != uid ||
pp->p_cred->cr_gid != gid ||
pp->p_cred->cr_suid != uid ||
pp->p_cred->cr_sgid != gid)) {
*uidp = uid;
*gidp = gid;
return (1);
}
return (0);
}
int
execpermissions(vp, vattrp, args)
struct vnode *vp;
struct vattr *vattrp;
struct uarg *args;
{
int error;
register proc_t *p = ttoproc(curthread);
vattrp->va_mask = AT_MODE|AT_UID|AT_GID|AT_SIZE;
if (error = VOP_GETATTR(vp, vattrp, ATTR_EXEC, p->p_cred))
return (error);
/*
* Check the access mode.
*/
if ((error = VOP_ACCESS(vp, VEXEC, 0, p->p_cred)) != 0 ||
vp->v_type != VREG || (vattrp->va_mode &
(VEXEC|(VEXEC>>3)|(VEXEC>>6))) == 0) {
if (error == 0)
error = EACCES;
return (error);
}
if ((p->p_plist || (p->p_flag & (STRC|SPROCTR))) &&
(error = VOP_ACCESS(vp, VREAD, 0, p->p_cred))) {
/*
* If process is under ptrace(2) compatibility,
* fail the exec(2).
*/
if (p->p_flag & STRC)
goto bad;
/*
* Process is traced via /proc.
* Arrange to invalidate the /proc vnode.
*/
args->traceinval = 1;
}
return (0);
bad:
if (error == 0)
error = ENOEXEC;
return (error);
}
/*
* Map a section of an executable file into the user's
* address space.
*/
int
execmap(vp, addr, len, zfodlen, offset, prot, page)
struct vnode *vp;
caddr_t addr;
size_t len, zfodlen;
off_t offset;
int prot;
int page;
{
int error = 0;
off_t oldoffset;
caddr_t zfodbase, oldaddr;
size_t end, oldlen;
int zfoddiff;
label_t ljb;
register proc_t *p = ttoproc(curthread);
oldaddr = addr;
addr = (caddr_t)((long)addr & PAGEMASK);
if (len) {
oldlen = len;
len += ((size_t)oldaddr - (size_t)addr);
oldoffset = offset;
offset = (off_t)((long)offset & PAGEMASK);
if (page) {
int npages, preread, prefltmem, availm;
if (error = VOP_MAP(vp, (offset_t)offset,
p->p_as, &addr, len, prot, PROT_ALL,
MAP_PRIVATE | MAP_FIXED, CRED()))
goto bad;
/*
* If the segment can fit, then we prefault
* the entire segment in. This is based on the
* model that says the best working set of a
* small program is all of its pages.
*/
npages = btopr(len);
prefltmem = freemem - desfree;
preread =
(npages < prefltmem && len < PGTHRESH) ? 1 : 0;
/*
* If we aren't prefaulting the segment,
* increment "deficit", if necessary to ensure
* that pages will become available when this
* process starts executing.
*/
availm = freemem - lotsfree;
if (preread == 0 && npages > availm &&
deficit < lotsfree) {
deficit += MIN(npages - availm,
lotsfree - deficit);
}
if (preread) {
TRACE_2(TR_FAC_PROC, TR_EXECMAP_PREREAD,
"execmap preread:freemem %d size %d",
freemem, len);
(void) as_fault(p->p_as->a_hat, p->p_as,
(caddr_t)addr, len, F_INVAL, S_READ);
}
#ifdef TRACE
else {
TRACE_2(TR_FAC_PROC, TR_EXECMAP_NO_PREREAD,
"execmap no preread:freemem %d size %d",
freemem, len);
}
#endif
} else {
if (error = as_map(p->p_as, addr, len,
segvn_create, zfod_argsp))
goto bad;
/*
* Read in the segment in one big chunk.
*/
if (error = vn_rdwr(UIO_READ, vp, (caddr_t)oldaddr,
oldlen, oldoffset, UIO_USERSPACE, 0,
(long)0, CRED(), (int *)0))
goto bad;
/*
* Now set protections.
*/
if (prot != PROT_ALL) {
(void) as_setprot(p->p_as, (caddr_t)addr,
len, prot);
}
}
}
if (zfodlen) {
end = (size_t)addr + len;
zfodbase = (caddr_t)roundup(end, PAGESIZE);
zfoddiff = (int)zfodbase - end;
if (zfoddiff > 0) {
#ifdef i386
(void) forcefault((caddr_t)end, zfoddiff);
#endif
if (on_fault(&ljb))
goto bad;
(void) uzero((caddr_t)end, zfoddiff);
no_fault();
}
if (zfodlen > zfoddiff) {
zfodlen -= zfoddiff;
if (error = as_map(p->p_as, (caddr_t)zfodbase,
zfodlen, segvn_create, zfod_argsp))
goto bad;
if (prot != PROT_ALL) {
(void) as_setprot(p->p_as, (caddr_t)zfodbase,
zfodlen, prot);
}
}
}
return (0);
bad:
return (error);
}
void
setexecenv(ep)
struct execenv *ep;
{
proc_t *p = ttoproc(curthread);
klwp_id_t lwp = ttolwp(curthread);
struct vnode *vp;
p->p_brkbase = ep->ex_brkbase;
p->p_brksize = ep->ex_brksize;
if (p->p_exec)
VN_RELE(p->p_exec); /* out with the old */
vp = p->p_exec = ep->ex_vp;
if (vp)
VN_HOLD(vp); /* in with the new */
lwp->lwp_sigaltstack.ss_sp = 0;
lwp->lwp_sigaltstack.ss_size = 0;
lwp->lwp_sigaltstack.ss_flags = SS_DISABLE;
p->p_user.u_execid = (int)ep->ex_magic;
}
int
execopen(vpp, fdp)
struct vnode **vpp;
int *fdp;
{
struct vnode *vp = *vpp;
file_t *fp;
int error = 0;
int filemode = FREAD;
VN_HOLD(vp); /* open reference */
if (error = falloc((struct vnode *)NULL, filemode, &fp, fdp)) {
VN_RELE(vp);
*fdp = -1; /* just in case falloc changed value */
return (error);
}
if (error = VOP_OPEN(&vp, filemode, CRED())) {
VN_RELE(vp);
setf(*fdp, NULLFP);
unfalloc(fp);
*fdp = -1;
return (error);
}
*vpp = vp; /* vnode should not have changed */
fp->f_vnode = vp;
mutex_exit(&fp->f_tlock);
setf(*fdp, fp);
return (0);
}
int
execclose(fd)
int fd;
{
file_t *fp;
if (fp = getandset(fd))
return (closef(fp));
else
return (EBADF);
}
/*
* noexec stub function.
*/
/* ARGSUSED */
int
noexec(vp, uap, args, idatap, level, execsz, setid, exec_file, cred)
struct vnode *vp;
struct execa *uap;
struct uarg *args;
struct intpdata *idatap;
int level;
long *execsz;
int setid;
caddr_t exec_file;
struct cred *cred;
{
cmn_err(CE_WARN, "missing exec capability for %s\n", uap->fname);
return (ENOEXEC);
}