#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sysmsg.h>
#include <sys/kernel.h>
#include <sys/mount.h>
#include <sys/filedesc.h>
#include <sys/fcntl.h>
#include <sys/acct.h>
#include <sys/exec.h>
#include <sys/imgact.h>
#include <sys/imgact_elf.h>
#include <sys/kern_syscall.h>
#include <sys/wait.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/ktrace.h>
#include <sys/signalvar.h>
#include <sys/pioctl.h>
#include <sys/nlookup.h>
#include <sys/sysent.h>
#include <sys/shm.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/vmmeter.h>
#include <sys/libkern.h>
#include <cpu/lwbuf.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.h>
#include <vm/pmap.h>
#include <vm/vm_page.h>
#include <vm/vm_map.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/vm_object.h>
#include <vm/vnode_pager.h>
#include <vm/vm_pager.h>
#include <sys/reg.h>
#include <sys/objcache.h>
#include <sys/refcount.h>
#include <sys/thread2.h>
#include <vm/vm_page2.h>
MALLOC_DEFINE(M_PARGS, "proc-args", "Process arguments");
MALLOC_DEFINE(M_EXECARGS, "exec-args", "Exec arguments");
enum exec_path_segflg {
PATH_SYSSPACE,
PATH_USERSPACE,
};
static register_t *exec_copyout_strings(struct image_params *);
static int exec_copyin_args(struct image_args *, char *,
enum exec_path_segflg, char **, char **);
static void exec_free_args(struct image_args *);
static void print_execve_args(struct image_args *args);
__read_mostly static u_long ps_strings = PS_STRINGS;
SYSCTL_ULONG(_kern, KERN_PS_STRINGS, ps_strings, CTLFLAG_RD, &ps_strings, 0, "");
__read_mostly static u_long usrstack = USRSTACK;
SYSCTL_ULONG(_kern, KERN_USRSTACK, usrstack, CTLFLAG_RD, &usrstack, 0, "");
__read_mostly u_long ps_arg_cache_limit = PAGE_SIZE / 16;
SYSCTL_LONG(_kern, OID_AUTO, ps_arg_cache_limit, CTLFLAG_RW,
&ps_arg_cache_limit, 0, "");
__read_mostly int ps_argsopen = 1;
SYSCTL_INT(_kern, OID_AUTO, ps_argsopen, CTLFLAG_RW, &ps_argsopen, 0, "");
__read_mostly static int ktrace_suid = 0;
SYSCTL_INT(_kern, OID_AUTO, ktrace_suid, CTLFLAG_RW, &ktrace_suid, 0, "");
__read_mostly static int debug_execve_args = 0;
SYSCTL_INT(_kern, OID_AUTO, debug_execve_args, CTLFLAG_RW, &debug_execve_args,
0, "");
__read_mostly static struct objcache *exec_objcache;
static
void
exec_objcache_init(void *arg __unused)
{
int cluster_limit;
size_t limsize;
cluster_limit = (ncpus < 16) ? 16 : ncpus;
limsize = kmem_lim_size();
if (limsize > 7 * 1024)
cluster_limit *= 2;
if (limsize > 15 * 1024)
cluster_limit *= 2;
exec_objcache = objcache_create_mbacked(
M_EXECARGS, PATH_MAX + ARG_MAX,
cluster_limit, 8,
NULL, NULL, NULL);
}
SYSINIT(exec_objcache, SI_BOOT2_MACHDEP, SI_ORDER_ANY, exec_objcache_init, 0);
__read_mostly static int stackgap_random = 1024;
static int
sysctl_kern_stackgap(SYSCTL_HANDLER_ARGS)
{
int error, new_val;
new_val = stackgap_random;
error = sysctl_handle_int(oidp, &new_val, 0, req);
if (error != 0 || req->newptr == NULL)
return (error);
if (new_val > 0 && ((new_val > 16 * PAGE_SIZE) || !powerof2(new_val)))
return (EINVAL);
stackgap_random = new_val;
return(0);
}
SYSCTL_PROC(_kern, OID_AUTO, stackgap_random, CTLFLAG_RW|CTLTYPE_INT,
0, 0, sysctl_kern_stackgap, "I",
"Max random stack gap (power of 2), static gap if negative");
static void
print_execve_args(struct image_args *args)
{
char *cp;
int ndx;
cp = args->begin_argv;
for (ndx = 0; ndx < args->argc; ndx++) {
kprintf("\targv[%d]: %s\n", ndx, cp);
while (*cp++ != '\0');
}
for (ndx = 0; ndx < args->envc; ndx++) {
kprintf("\tenvv[%d]: %s\n", ndx, cp);
while (*cp++ != '\0');
}
}
__read_mostly static const struct execsw **execsw;
int
kern_execve(struct nlookupdata *nd, struct file *fp, char fileflags,
struct image_args *args)
{
static const char *proctitle = "(execve)";
register_t *stack_base;
struct thread *td = curthread;
struct lwp *lp = td->td_lwp;
struct proc *p = td->td_proc;
struct vnode *ovp;
struct pargs *pa;
struct sigacts *ops;
struct sigacts *nps;
struct image_params image_params, *imgp;
struct filedesc *fds;
struct nchandle *nch;
struct nlookupdata nd_interpreter;
struct vattr_lite lva;
int error, len, i;
int (*img_first) (struct image_params *);
if (debug_execve_args) {
kprintf("%s()\n", __func__);
print_execve_args(args);
}
KKASSERT(p);
lwkt_gettoken(&p->p_token);
imgp = &image_params;
imgp->proc = p;
imgp->args = args;
imgp->lvap = &lva;
imgp->entry_addr = 0;
imgp->resident = 0;
imgp->vmspace_destroyed = 0;
imgp->interpreted = 0;
imgp->interpreter_name[0] = 0;
imgp->auxargs = NULL;
imgp->vp = NULL;
imgp->firstpage = NULL;
imgp->ps_strings = 0;
imgp->execpath = imgp->freepath = NULL;
imgp->execpathp = 0;
imgp->image_header = NULL;
interpret:
if (nd) {
nch = &nd->nl_nch;
nd->nl_flags |= NLC_SHAREDLOCK;
if ((error = nlookup(nd)) != 0)
goto failed;
error = cache_vget(nch, nd->nl_cred, LK_SHARED, &imgp->vp);
KKASSERT(nd->nl_flags & NLC_NCPISLOCKED);
nd->nl_flags &= ~NLC_NCPISLOCKED;
cache_unlock(nch);
} else {
nch = &fp->f_nchandle;
imgp->vp = fp->f_data;
error = vget(imgp->vp, LK_SHARED);
}
if (error) {
imgp->vp = NULL;
goto failed;
}
error = exec_check_permissions(imgp, nch->mount);
if (error) {
vn_unlock(imgp->vp);
goto failed;
}
error = exec_map_first_page(imgp);
vn_unlock(imgp->vp);
if (error)
goto failed;
imgp->proc->p_osrel = 0;
if (debug_execve_args && imgp->interpreted) {
kprintf(" target is interpreted -- recursive pass\n");
kprintf(" interpreter: %s\n", imgp->interpreter_name);
print_execve_args(args);
}
error = -1;
if ((img_first = imgp->proc->p_sysent->sv_imgact_try) != NULL)
error = img_first(imgp);
if (error == -1 && imgp->vp->v_resident)
error = exec_resident_imgact(imgp);
for (i = 0; error == -1 && execsw[i]; ++i) {
if (execsw[i]->ex_imgact == NULL ||
execsw[i]->ex_imgact == img_first) {
continue;
}
error = (*execsw[i]->ex_imgact)(imgp);
}
if (error) {
if (error == -1)
error = ENOEXEC;
goto failed;
}
if (imgp->interpreted) {
exec_unmap_first_page(imgp);
vrele(imgp->vp);
imgp->vp = NULL;
nd = &nd_interpreter;
error = nlookup_init(nd, imgp->interpreter_name,
UIO_SYSSPACE, NLC_FOLLOW);
if (error)
goto failed;
if (fp && (fileflags & UF_EXCLOSE)) {
error = ENOENT;
goto failed;
}
goto interpret;
}
if (imgp->auxargs != NULL &&
((args->fname != NULL && args->fname[0] == '/') ||
vn_fullpath(imgp->proc, imgp->vp, &imgp->execpath,
&imgp->freepath, 0) != 0))
{
imgp->execpath = args->fname;
}
stack_base = exec_copyout_strings(imgp);
p->p_vmspace->vm_minsaddr = (char *)stack_base;
if (p->p_sysent->sv_fixup && imgp->resident == 0)
(*p->p_sysent->sv_fixup)(&stack_base, imgp);
else
suword64(--stack_base, imgp->args->argc);
if (p->p_fd->fd_refcnt > 1) {
if ((error = fdcopy(p, &fds)) != 0)
goto failed;
fdfree(p, fds);
}
ops = p->p_sigacts;
if (ops->ps_refcnt > 1) {
nps = kmalloc(sizeof(*nps), M_SUBPROC, M_WAITOK);
bcopy(ops, nps, sizeof(*nps));
refcount_init(&nps->ps_refcnt, 1);
p->p_sigacts = nps;
if (refcount_release(&ops->ps_refcnt)) {
kfree(ops, M_SUBPROC);
ops = NULL;
}
}
lwp_userunmap(lp);
proc_userunmap(p);
if (p->p_vkernel)
vkernel_exit(p);
stopprofclock(p);
fdcloseexec(p);
execsigs(p);
if (nch->ncp) {
len = min(nch->ncp->nc_nlen, MAXCOMLEN);
bcopy(nch->ncp->nc_name, p->p_comm, len);
} else {
len = sizeof(proctitle) - 1;
bcopy(proctitle, p->p_comm, len);
}
p->p_comm[len] = 0;
bcopy(p->p_comm, lp->lwp_thread->td_comm, MAXCOMLEN+1);
p->p_flags |= P_EXEC;
if (p->p_pptr && (p->p_flags & P_PPWAIT)) {
if (p->p_pptr->p_upmap)
atomic_add_int(&p->p_pptr->p_upmap->invfork, -1);
atomic_clear_int(&p->p_flags, P_PPWAIT);
wakeup(p->p_pptr);
}
if ((((lva.va_mode & VSUID) && p->p_ucred->cr_uid != lva.va_uid) ||
((lva.va_mode & VSGID) && p->p_ucred->cr_gid != lva.va_gid)) &&
(imgp->vp->v_mount->mnt_flag & MNT_NOSUID) == 0 &&
(p->p_flags & P_TRACED) == 0) {
setsugid();
if (p->p_tracenode && ktrace_suid == 0 &&
caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT) != 0)
{
ktrdestroy(&p->p_tracenode);
p->p_traceflag = 0;
}
p->p_deathsig = 0;
setugidsafety(p);
error = fdcheckstd(lp);
if (error != 0)
goto failed;
cratom_proc(p);
if (lva.va_mode & VSUID)
change_euid(lva.va_uid);
if (lva.va_mode & VSGID)
p->p_ucred->cr_gid = lva.va_gid;
varsymset_clean(&p->p_varsymset);
} else {
if (p->p_ucred->cr_uid == p->p_ucred->cr_ruid &&
p->p_ucred->cr_gid == p->p_ucred->cr_rgid)
p->p_flags &= ~P_SUGID;
}
if (p->p_ucred->cr_svuid != p->p_ucred->cr_uid ||
p->p_ucred->cr_svgid != p->p_ucred->cr_gid) {
cratom_proc(p);
p->p_ucred->cr_svuid = p->p_ucred->cr_uid;
p->p_ucred->cr_svgid = p->p_ucred->cr_gid;
}
ovp = p->p_textvp;
vref(imgp->vp);
p->p_textvp = imgp->vp;
if (ovp)
vrele(ovp);
if (p->p_textnch.ncp)
cache_drop(&p->p_textnch);
if (nch->mount)
cache_copy(nch, &p->p_textnch);
caps_exec(p);
KNOTE(&p->p_klist, NOTE_EXEC);
p->p_flags &= ~P_INEXEC;
if (p->p_stops)
wakeup(&p->p_stype);
STOPEVENT(p, S_EXEC, 0);
if (p->p_flags & P_TRACED)
ksignal(p, SIGTRAP);
p->p_acflag &= ~AFORK;
exec_setregs(imgp->entry_addr, (u_long)(uintptr_t)stack_base,
imgp->ps_strings);
vn_mark_atime(imgp->vp, td);
pa = p->p_args;
p->p_args = NULL;
if (pa && refcount_release(&pa->ar_ref)) {
kfree(pa, M_PARGS);
pa = NULL;
}
i = imgp->args->begin_envv - imgp->args->begin_argv;
if (sizeof(struct pargs) + i <= ps_arg_cache_limit) {
pa = kmalloc(sizeof(struct pargs) + i, M_PARGS, M_WAITOK);
refcount_init(&pa->ar_ref, 1);
pa->ar_length = i;
bcopy(imgp->args->begin_argv, pa->ar_args, i);
KKASSERT(p->p_args == NULL);
p->p_args = pa;
}
failed:
if (imgp->firstpage)
exec_unmap_first_page(imgp);
if (imgp->vp)
vrele(imgp->vp);
if (imgp->freepath)
kfree(imgp->freepath, M_TEMP);
if (nd == &nd_interpreter)
nlookup_done(nd);
if (error == 0) {
++mycpu->gd_cnt.v_exec;
lwkt_reltoken(&p->p_token);
return (0);
}
if (imgp->vmspace_destroyed & 2) {
p->p_flags &= ~P_INEXEC;
if (p->p_stops)
wakeup(&p->p_stype);
}
lwkt_reltoken(&p->p_token);
if (imgp->vmspace_destroyed) {
return (-1);
} else {
return (error);
}
}
int
sys_execve(struct sysmsg *sysmsg, const struct execve_args *uap)
{
struct nlookupdata nd;
struct image_args args;
int error;
if (caps_priv_check_self(SYSCAP_NOEXEC | __SYSCAP_NOROOTTEST))
return EACCES;
bzero(&args, sizeof(args));
error = nlookup_init(&nd, uap->fname, UIO_USERSPACE, NLC_FOLLOW);
if (error == 0) {
error = exec_copyin_args(&args, uap->fname, PATH_USERSPACE,
uap->argv, uap->envv);
}
if (error == 0)
error = kern_execve(&nd, NULL, 0, &args);
nlookup_done(&nd);
exec_free_args(&args);
if (error < 0) {
exit1(W_EXITCODE(0, SIGABRT));
}
if (error == 0)
sysmsg->sysmsg_result64 = 0;
return (error);
}
int
sys_fexecve(struct sysmsg *sysmsg, const struct fexecve_args *uap)
{
struct image_args args;
struct thread *td = curthread;
struct file *fp;
char fileflags;
char fname[32];
int error;
if (caps_priv_check_self(SYSCAP_NOEXEC | __SYSCAP_NOROOTTEST))
return EACCES;
if ((error = holdvnode2(td, uap->fd, &fp, &fileflags)) != 0)
return (error);
if ((fp->f_flag & FWRITE) != 0 || (fp->f_flag & FREAD) == 0) {
error = EBADF;
goto done;
}
ksnprintf(fname, sizeof(fname), "/dev/fd/%d", uap->fd);
bzero(&args, sizeof(args));
error = exec_copyin_args(&args, fname, PATH_SYSSPACE,
uap->argv, uap->envv);
if (error == 0)
error = kern_execve(NULL, fp, fileflags, &args);
exec_free_args(&args);
if (error < 0) {
exit1(W_EXITCODE(0, SIGABRT));
}
if (error == 0)
sysmsg->sysmsg_result64 = 0;
done:
fdrop(fp);
return (error);
}
int
exec_map_page(struct image_params *imgp, vm_pindex_t pageno,
struct lwbuf **plwb, const char **pdata)
{
int rv;
vm_page_t ma;
vm_page_t m;
vm_object_t object;
if ((object = imgp->vp->v_object) == NULL)
return (EIO);
if (pageno >= object->size)
return (EIO);
vm_object_hold_shared(object);
m = vm_page_lookup(object, pageno);
if (m) {
if ((m->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL) {
vm_page_hold(m);
vm_page_sleep_busy(m, FALSE, "execpg");
if ((m->valid & VM_PAGE_BITS_ALL) == VM_PAGE_BITS_ALL &&
m->object == object && m->pindex == pageno) {
vm_object_drop(object);
goto done;
}
vm_page_unhold(m);
}
}
vm_object_drop(object);
vm_object_hold(object);
m = vm_page_grab(object, pageno, VM_ALLOC_NORMAL | VM_ALLOC_RETRY);
while ((m->valid & VM_PAGE_BITS_ALL) != VM_PAGE_BITS_ALL) {
ma = m;
rv = vm_pager_get_page(object, pageno, &ma, 1);
m = vm_page_lookup(object, pageno);
if (rv != VM_PAGER_OK || m == NULL || m->valid == 0) {
if (m) {
vm_page_protect(m, VM_PROT_NONE);
vnode_pager_freepage(m);
}
vm_object_drop(object);
return EIO;
}
}
vm_page_hold(m);
vm_page_wakeup(m);
vm_object_drop(object);
done:
*plwb = lwbuf_alloc(m, *plwb);
*pdata = (void *)lwbuf_kva(*plwb);
return (0);
}
int
exec_map_first_page(struct image_params *imgp)
{
int err;
if (imgp->firstpage)
exec_unmap_first_page(imgp);
imgp->firstpage = &imgp->firstpage_cache;
err = exec_map_page(imgp, 0, &imgp->firstpage, &imgp->image_header);
if (err) {
imgp->firstpage = NULL;
return err;
}
return 0;
}
void
exec_unmap_page(struct lwbuf *lwb)
{
vm_page_t m;
crit_enter();
if (lwb != NULL) {
m = lwbuf_page(lwb);
lwbuf_free(lwb);
vm_page_unhold(m);
}
crit_exit();
}
void
exec_unmap_first_page(struct image_params *imgp)
{
exec_unmap_page(imgp->firstpage);
imgp->firstpage = NULL;
imgp->image_header = NULL;
}
int
exec_new_vmspace(struct image_params *imgp, struct vmspace *vmcopy)
{
struct vmspace *vmspace = imgp->proc->p_vmspace;
vm_offset_t stack_addr = USRSTACK - maxssiz;
struct lwp *lp;
struct proc *p;
vm_map_t map;
int error;
lp = curthread->td_lwp;
p = lp->lwp_proc;
imgp->vmspace_destroyed = 1;
if (curthread->td_proc->p_nthreads > 1) {
error = killalllwps(1);
if (error)
return (error);
}
imgp->vmspace_destroyed |= 2;
p->p_flags |= P_INEXEC;
if (p->p_stops)
wakeup(&p->p_stype);
PSTALL(p, "exec1", ((p->p_flags & P_PPWAIT) ? 1 : 0));
map = &vmspace->vm_map;
if (vmcopy) {
vmspace_exec(imgp->proc, vmcopy);
vmspace = imgp->proc->p_vmspace;
pmap_remove_pages(vmspace_pmap(vmspace), stack_addr, USRSTACK);
map = &vmspace->vm_map;
} else if (vmspace_getrefs(vmspace) == 1) {
shmexit(vmspace);
pmap_remove_pages(vmspace_pmap(vmspace),
0, VM_MAX_USER_ADDRESS);
vm_map_remove(map, 0, VM_MAX_USER_ADDRESS);
} else {
vmspace_exec(imgp->proc, NULL);
vmspace = imgp->proc->p_vmspace;
map = &vmspace->vm_map;
}
lwp_userunmap(lp);
proc_userunmap(p);
lp->lwp_tid = 1;
p->p_lasttid = 1;
error = vm_map_stack(&vmspace->vm_map, &stack_addr, (vm_size_t)maxssiz,
0,
VM_PROT_READ|VM_PROT_WRITE,
VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE,
0);
if (error)
return (error);
vmspace->vm_ssize = sgrowsiz;
vmspace->vm_maxsaddr = (char *)USRSTACK - maxssiz;
return(0);
}
static int
exec_copyin_args(struct image_args *args, char *fname,
enum exec_path_segflg segflg, char **argv, char **envv)
{
char *argp, *envp;
int error = 0;
size_t length;
args->buf = objcache_get(exec_objcache, M_WAITOK);
if (args->buf == NULL)
return (ENOMEM);
args->begin_argv = args->buf;
args->endp = args->begin_argv;
args->space = ARG_MAX;
args->fname = args->buf + ARG_MAX;
if (segflg == PATH_SYSSPACE)
error = copystr(fname, args->fname, PATH_MAX, &length);
else
error = copyinstr(fname, args->fname, PATH_MAX, &length);
if (error)
return (error);
if (argv == NULL)
error = EFAULT;
if (error == 0) {
while ((argp = (caddr_t)(intptr_t)
fuword64((uintptr_t *)argv++)) != NULL) {
if (argp == (caddr_t)-1) {
error = EFAULT;
break;
}
error = copyinstr(argp, args->endp,
args->space, &length);
if (error) {
if (error == ENAMETOOLONG)
error = E2BIG;
break;
}
args->space -= length;
args->endp += length;
args->argc++;
}
if (args->argc == 0 && error == 0) {
length = strlen(args->fname) + 1;
if (length > args->space) {
error = E2BIG;
} else {
bcopy(args->fname, args->endp, length);
args->space -= length;
args->endp += length;
args->argc++;
}
}
}
args->begin_envv = args->endp;
if (envv && error == 0) {
while ((envp = (caddr_t)(intptr_t)
fuword64((uintptr_t *)envv++))) {
if (envp == (caddr_t) -1) {
error = EFAULT;
break;
}
error = copyinstr(envp, args->endp,
args->space, &length);
if (error) {
if (error == ENAMETOOLONG)
error = E2BIG;
break;
}
args->space -= length;
args->endp += length;
args->envc++;
}
}
return (error);
}
static void
exec_free_args(struct image_args *args)
{
if (args->buf) {
objcache_put(exec_objcache, args->buf);
args->buf = NULL;
}
}
static register_t *
exec_copyout_strings(struct image_params *imgp)
{
int argc, envc, sgap;
int gap;
int argsenvspace;
char **vectp;
char *stringp, *destp, *szsigbase;
register_t *stack_base;
struct ps_strings *arginfo;
size_t execpath_len;
int szsigcode;
if (imgp->execpath != NULL && imgp->auxargs != NULL)
execpath_len = strlen(imgp->execpath) + 1;
else
execpath_len = 0;
arginfo = (struct ps_strings *)PS_STRINGS;
szsigcode = *(imgp->proc->p_sysent->sv_szsigcode);
argsenvspace = roundup((ARG_MAX - imgp->args->space), sizeof(char *));
gap = stackgap_random;
cpu_ccfence();
if (gap != 0) {
if (gap < 0)
sgap = ALIGN(-gap);
else
sgap = ALIGN(karc4random() & (gap - 1));
} else {
sgap = 0;
}
szsigbase = (char *)(intptr_t)
trunc_page64((intptr_t)arginfo - szsigcode);
szsigbase -= SZSIGCODE_EXTRA_BYTES;
destp = szsigbase -
roundup(execpath_len, sizeof(char *)) -
SPARE_USRSPACE -
sgap -
argsenvspace;
if (szsigcode)
copyout(imgp->proc->p_sysent->sv_sigcode, szsigbase, szsigcode);
if (execpath_len) {
imgp->execpathp = (uintptr_t)szsigbase -
roundup(execpath_len, sizeof(char *));
copyout(imgp->execpath, (void *)imgp->execpathp, execpath_len);
}
vectp = (char **)destp - (AT_COUNT * 2);
vectp -= imgp->args->argc + imgp->args->envc + 2;
stack_base = (register_t *)vectp;
stringp = imgp->args->begin_argv;
argc = imgp->args->argc;
envc = imgp->args->envc;
copyout(stringp, destp, ARG_MAX - imgp->args->space);
suword64((void *)&arginfo->ps_argvstr, (uint64_t)(intptr_t)vectp);
suword32((void *)&arginfo->ps_nargvstr, argc);
for (; argc > 0; --argc) {
suword64((void *)vectp++, (uintptr_t)destp);
while (*stringp++ != 0)
destp++;
destp++;
}
suword64((void *)vectp++, 0);
suword64((void *)&arginfo->ps_envstr, (uintptr_t)vectp);
suword32((void *)&arginfo->ps_nenvstr, envc);
for (; envc > 0; --envc) {
suword64((void *)vectp++, (uintptr_t)destp);
while (*stringp++ != 0)
destp++;
destp++;
}
suword64((void *)vectp, 0);
vm_map_protect(&imgp->proc->p_vmspace->vm_map,
(vm_offset_t)szsigbase,
(vm_offset_t)szsigbase + PAGE_SIZE,
VM_PROT_READ|VM_PROT_EXECUTE, FALSE);
return (stack_base);
}
int
exec_check_permissions(struct image_params *imgp, struct mount *topmnt)
{
struct proc *p = imgp->proc;
struct vnode *vp = imgp->vp;
struct vattr_lite *lvap = imgp->lvap;
int error;
error = VOP_GETATTR_LITE(vp, lvap);
if (error)
return (error);
if ((vp->v_mount->mnt_flag & MNT_NOEXEC) ||
((topmnt != NULL) && (topmnt->mnt_flag & MNT_NOEXEC)) ||
((lvap->va_mode & 0111) == 0) ||
(lvap->va_type != VREG)) {
return (EACCES);
}
if ((lvap->va_mode & VSUID) && caps_priv_check_self(SYSCAP_NOEXEC_SUID | __SYSCAP_NOROOTTEST))
return EACCES;
if ((lvap->va_mode & VSGID) && caps_priv_check_self(SYSCAP_NOEXEC_SGID | __SYSCAP_NOROOTTEST))
return EACCES;
if (lvap->va_size == 0)
return (ENOEXEC);
error = VOP_EACCESS(vp, VEXEC, p->p_ucred);
if (error)
return (error);
if (vp->v_writecount)
return (ETXTBSY);
error = VOP_OPEN(vp, FREAD, p->p_ucred, NULL);
if (error)
return (error);
return (0);
}
int
exec_register(const struct execsw *execsw_arg)
{
const struct execsw **es, **xs, **newexecsw;
int count = 2;
if (execsw)
for (es = execsw; *es; es++)
count++;
newexecsw = kmalloc(count * sizeof(*es), M_TEMP, M_WAITOK);
xs = newexecsw;
if (execsw)
for (es = execsw; *es; es++)
*xs++ = *es;
*xs++ = execsw_arg;
*xs = NULL;
if (execsw)
kfree(execsw, M_TEMP);
execsw = newexecsw;
return 0;
}
int
exec_unregister(const struct execsw *execsw_arg)
{
const struct execsw **es, **xs, **newexecsw;
int count = 1;
if (execsw == NULL)
panic("unregister with no handlers left?");
for (es = execsw; *es; es++) {
if (*es == execsw_arg)
break;
}
if (*es == NULL)
return ENOENT;
for (es = execsw; *es; es++)
if (*es != execsw_arg)
count++;
newexecsw = kmalloc(count * sizeof(*es), M_TEMP, M_WAITOK);
xs = newexecsw;
for (es = execsw; *es; es++)
if (*es != execsw_arg)
*xs++ = *es;
*xs = NULL;
if (execsw)
kfree(execsw, M_TEMP);
execsw = newexecsw;
return 0;
}