* Copyright (c) 1993, David Greenman
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by David Greenman
* 4. The name of the developer may be used to endorse or promote products
* derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* $Id: kern_execve.c,v 1.16 1994/03/15 01:58:29 wollman Exp $
int exec_extract_strings
__P((struct image_params
*));
int *exec_copyout_strings
__P((struct image_params
*));
* execsw_set is constructed for us by the linker. Each of the items
* is a pointer to a `const struct execsw', hence the double pointer here.
extern const struct linker_set execsw_set
;
const struct execsw
**execsw
= (const struct execsw
**)&execsw_set
.ls_items
[0];
register struct execve_args
*uap
;
struct nameidata nd
, *ndp
;
char *stringbase
, *stringp
;
int error
, resid
, len
, i
;
struct image_params image_params
, *iparams
;
bzero((caddr_t
)iparams
, sizeof(struct image_params
));
image_header
= (char *)0;
* Initialize a few constants in the common area
* Allocate temporary demand zeroed space for argument and
error
= vm_allocate(kernel_map
, (vm_offset_t
*)&iparams
->stringbase
,
log(LOG_WARNING
, "execve: failed to allocate string space\n");
if (!iparams
->stringbase
) {
iparams
->stringp
= iparams
->stringbase
;
iparams
->stringspace
= ARG_MAX
;
* Translate the file name. namei() returns a vnode pointer
* in ni_vp amoung other things.
ndp
->ni_nameiop
= LOOKUP
| LOCKLEAF
| FOLLOW
| SAVENAME
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
vm_deallocate(kernel_map
, (vm_offset_t
)iparams
->stringbase
,
iparams
->vnodep
= vnodep
= ndp
->ni_vp
;
* Check file permissions (also 'opens' file)
error
= exec_check_permissions(iparams
);
* Read the image header from the file.
error
= vn_rdwr(UIO_READ
,
UIO_SYSSPACE
, IO_NODELOCKED
,
/* Clear out junk in image_header if a partial read (small file) */
bzero(image_header
+ (sizeof(image_header
) - resid
), resid
);
* Map the image header (first page) of the file into
error
= vm_mmap(kernel_map
, /* map */
(vm_offset_t
*)&image_header
, /* address */
VM_PROT_READ
, /* protection */
VM_PROT_READ
, /* max protection */
(caddr_t
)vnodep
, /* vnode */
uprintf("mmap failed: %d\n",error
);
iparams
->image_header
= image_header
;
* Loop through list of image activators, calling each one.
* If there is no match, the activator returns -1. If there
* is a match, but there was an error during the activation,
* the error is returned. Otherwise 0 means success. If the
* image is interpreted, loop back up and try activating
for (i
= 0; execsw
[i
]; ++i
) {
if (execsw
[i
]->ex_imgact
)
error
= (*execsw
[i
]->ex_imgact
)(iparams
);
if (iparams
->interpreted
) {
/* free old vnode and name buffer */
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
if (vm_deallocate(kernel_map
,
(vm_offset_t
)image_header
, NBPG
))
panic("execve: header dealloc failed (1)");
/* set new name to that of the interpreter */
ndp
->ni_segflg
= UIO_SYSSPACE
;
ndp
->ni_dirp
= iparams
->interpreter_name
;
ndp
->ni_nameiop
= LOOKUP
| LOCKLEAF
| FOLLOW
| SAVENAME
;
/* If we made it through all the activators and none matched, exit. */
* Copy out strings (args and env) and initialize stack base
stack_base
= exec_copyout_strings(iparams
);
p
->p_vmspace
->vm_minsaddr
= (char *)stack_base
;
p
->p_vmspace
->vm_ssize
= (((caddr_t
)USRSTACK
- (char *)stack_base
) >> PAGE_SHIFT
) + 1;
* Stuff argument count as first item on stack
*(--stack_base
) = iparams
->argc
;
/* close files on exec, fixup signals */
/* name this process - nameiexec(p, ndp) */
len
= MIN(ndp
->ni_namelen
,MAXCOMLEN
);
bcopy(ndp
->ni_ptr
, p
->p_comm
, len
);
* mark as executable, wakeup any process that was vforked and tell
* it that it now has it's own resources back
if (p
->p_pptr
&& (p
->p_flag
& SPPWAIT
)) {
wakeup((caddr_t
)p
->p_pptr
);
/* implement set userid/groupid */
* Turn off kernel tracing for set-id programs, except for
if (p
->p_tracep
&& (attr
.va_mode
& (VSUID
| VSGID
)) &&
suser(p
->p_ucred
, &p
->p_acflag
)) {
if ((attr
.va_mode
&VSUID
) && (p
->p_flag
& STRC
) == 0) {
p
->p_ucred
= crcopy(p
->p_ucred
);
p
->p_ucred
->cr_uid
= attr
.va_uid
;
if ((attr
.va_mode
&VSGID
) && (p
->p_flag
& STRC
) == 0) {
p
->p_ucred
= crcopy(p
->p_ucred
);
p
->p_ucred
->cr_groups
[0] = attr
.va_gid
;
* Implement correct POSIX saved uid behavior.
p
->p_cred
->p_svuid
= p
->p_ucred
->cr_uid
;
p
->p_cred
->p_svgid
= p
->p_ucred
->cr_gid
;
/* mark vnode pure text */
ndp
->ni_vp
->v_flag
|= VTEXT
;
* If tracing the process, trap to debugger so breakpoints
* can be set before the program executes.
/* clear "fork but no exec" flag, as we _are_ execing */
/* clear `set id since last exec' flag */
setregs(p
, iparams
->entry_addr
, stack_base
);
* free various allocated resources
if (vm_deallocate(kernel_map
, (vm_offset_t
)iparams
->stringbase
,
panic("execve: string buffer dealloc failed (1)");
if (vm_deallocate(kernel_map
, (vm_offset_t
)image_header
, NBPG
))
panic("execve: header dealloc failed (2)");
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
if (iparams
->stringbase
&& iparams
->stringbase
!= (char *)-1)
if (vm_deallocate(kernel_map
, (vm_offset_t
)iparams
->stringbase
,
panic("execve: string buffer dealloc failed (2)");
if (iparams
->image_header
&& iparams
->image_header
!= (char *)-1)
if (vm_deallocate(kernel_map
,
(vm_offset_t
)iparams
->image_header
, NBPG
))
panic("execve: header dealloc failed (3)");
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
if (iparams
->vmspace_destroyed
) {
/* sorry, no more process anymore. exit gracefully */
vm_deallocate(&vs
->vm_map
, USRSTACK
- MAXSSIZ
, MAXSSIZ
);
kexit(p
, W_EXITCODE(0, SIGABRT
));
* Destroy old address space, and allocate a new stack
* The new stack is only DFLSSIZ large because it is grown
* automatically in trap.c.
exec_new_vmspace(iparams
)
struct image_params
*iparams
;
struct vmspace
*vmspace
= iparams
->proc
->p_vmspace
;
caddr_t stack_addr
= (caddr_t
) (USRSTACK
- DFLSSIZ
);
iparams
->vmspace_destroyed
= 1;
/* Blow away entire process VM */
vm_deallocate(&vmspace
->vm_map
, 0, USRSTACK
);
/* Allocate a new stack */
error
= vm_allocate(&vmspace
->vm_map
, (vm_offset_t
*)&stack_addr
,
/* Initialize maximum stack address */
vmspace
->vm_maxsaddr
= (char *)USRSTACK
- MAXSSIZ
;
* Copy out argument and environment strings from the old process
* address space into the temporary string buffer.
exec_extract_strings(iparams
)
struct image_params
*iparams
;
* extract arguments first
argv
= iparams
->uap
->argv
;
while (argp
= (caddr_t
) fuword(argv
++)) {
if (argp
== (caddr_t
) -1)
if (copyinstr(argp
, iparams
->stringp
, iparams
->stringspace
,
&length
) == ENAMETOOLONG
)
iparams
->stringspace
-= length
;
iparams
->stringp
+= length
;
* extract environment strings
envv
= iparams
->uap
->envv
;
while (envp
= (caddr_t
) fuword(envv
++)) {
if (envp
== (caddr_t
) -1)
if (copyinstr(envp
, iparams
->stringp
, iparams
->stringspace
,
&length
) == ENAMETOOLONG
)
iparams
->stringspace
-= length
;
iparams
->stringp
+= length
;
* Copy strings out to the new process address space, constructing
* new arg and env vector tables. Return a pointer to the base
* so that it can be used as the initial stack pointer.
exec_copyout_strings(iparams
)
struct image_params
*iparams
;
int vect_table_size
, string_table_size
;
* Calculate string base and vector table pointers.
destp
= (caddr_t
) ((caddr_t
)USRSTACK
-
roundup((ARG_MAX
- iparams
->stringspace
), sizeof(char *)));
* The '+ 2' is for the null pointers at the end of each of the
* arg and env vector sets
vectp
= (char **) (destp
-
(iparams
->argc
+ iparams
->envc
+ 2) * sizeof(char *));
* vectp also becomes our initial stack base
stack_base
= (int *)vectp
;
stringp
= iparams
->stringbase
;
for (; argc
> 0; --argc
) {
while (*destp
++ = *stringp
++);
/* a null vector table pointer seperates the argp's from the envp's */
for (; envc
> 0; --envc
) {
while (*destp
++ = *stringp
++);
/* end of vector table is a null pointer */
* Check permissions of file to execute.
* Return 0 for success or error code on failure.
exec_check_permissions(iparams
)
struct image_params
*iparams
;
struct proc
*p
= iparams
->proc
;
struct vnode
*vnodep
= iparams
->vnodep
;
struct vattr
*attr
= iparams
->attr
;
* Check number of open-for-writes on the file and deny execution
if (vnodep
->v_writecount
) {
/* Get file attributes */
error
= VOP_GETATTR(vnodep
, attr
, p
->p_ucred
, p
);
* 1) Check if file execution is disabled for the filesystem that this
* 2) Insure that at least one execute bit is on - otherwise root
* will always succeed, and we don't want to happen unless the
* file really is executable.
* 3) Insure that the file is a regular file.
if ((vnodep
->v_mount
->mnt_flag
& MNT_NOEXEC
) ||
((attr
->va_mode
& 0111) == 0) ||
(attr
->va_type
!= VREG
)) {
* Zero length files can't be exec'd
* Disable setuid/setgid if the filesystem prohibits it or if
* the process is being traced.
if ((vnodep
->v_mount
->mnt_flag
& MNT_NOSUID
) || (p
->p_flag
& STRC
))
attr
->va_mode
&= ~(VSUID
| VSGID
);
* Check for execute permission to file based on current credentials.
* Then call filesystem specific open routine (which does nothing
error
= VOP_ACCESS(vnodep
, VEXEC
, p
->p_ucred
, p
);
error
= VOP_OPEN(vnodep
, FREAD
, p
->p_ucred
, p
);