* Copyright (c) 1989, 1990, 1991, 1992 William F. Jolitz, TeleMuse
* 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 software is a component of "386BSD" developed by
* William F. Jolitz, TeleMuse.
* 4. Neither the name of the developer nor the name "386BSD"
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS A COMPONENT OF 386BSD DEVELOPED BY WILLIAM F. JOLITZ
* AND IS INTENDED FOR RESEARCH AND EDUCATIONAL PURPOSES ONLY. THIS
* SOFTWARE SHOULD NOT BE CONSIDERED TO BE A COMMERCIAL PRODUCT.
* THE DEVELOPER URGES THAT USERS WHO REQUIRE A COMMERCIAL PRODUCT
* NOT MAKE USE OF THIS WORK.
* FOR USERS WHO WISH TO UNDERSTAND THE 386BSD SYSTEM DEVELOPED
* BY WILLIAM F. JOLITZ, WE RECOMMEND THE USER STUDY WRITTEN
* REFERENCES SUCH AS THE "PORTING UNIX TO THE 386" SERIES
* (BEGINNING JANUARY 1991 "DR. DOBBS JOURNAL", USA AND BEGINNING
* JUNE 1991 "UNIX MAGAZIN", GERMANY) BY WILLIAM F. JOLITZ AND
* LYNNE GREER JOLITZ, AS WELL AS OTHER BOOKS ON UNIX AND THE
* ON-LINE 386BSD USER MANUAL BEFORE USE. A BOOK DISCUSSING THE INTERNALS
* OF 386BSD ENTITLED "386BSD FROM THE INSIDE OUT" WILL BE AVAILABLE LATE 1992.
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``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 DEVELOPER 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
* 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.
* $Id: kern_execve.c,v 1.7 1993/10/19 00:58:51 nate Exp $
extern int dostacklimits
;
#define copyinoutstr copyinstr
register struct execve_args
*uap
;
register struct nameidata
*ndp
;
char **argbuf
, **argbufp
, *stringbuf
, *stringbufp
;
int needsenv
, limitonargs
, stringlen
, addr
, size
, len
,
rv
, amt
, argc
, tsize
, dsize
, bsize
, cnt
, file_offset
,
char shellname
[MAXINTERP
]; /* 05 Aug 92*/
char ex_shell
[MAXINTERP
]; /* #! and interpreter name */
* Step 1. Lookup filename to see if we have something to execute.
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
ndp
->ni_nameiop
= LOOKUP
| LOCKLEAF
| FOLLOW
| SAVENAME
;
if (ndp
->ni_vp
->v_writecount
) { /* don't exec if file is busy */
/* does it have any attributes? */
rv
= VOP_GETATTR(ndp
->ni_vp
, &attr
, p
->p_ucred
, p
);
if (ndp
->ni_vp
->v_mount
->mnt_flag
& MNT_NOEXEC
) { /* no exec on fs ?*/
/* is it executable, and a regular file? */
if ((ndp
->ni_vp
->v_mount
->mnt_flag
& MNT_NOEXEC
) || /* 29 Jul 92*/
(VOP_ACCESS(ndp
->ni_vp
, VEXEC
, p
->p_ucred
, p
)) ||
((attr
.va_mode
& 0111) == 0) ||
(attr
.va_type
!= VREG
)) {
* Step 2. Does the file contain a format we can
* Read in first few bytes of file for segment sizes, magic number:
* ZMAGIC = demand paged RO text
* Also an ASCII line beginning with #! is
* the file name of a ``shell'' and arguments may be prepended
* to the argument list if given here.
exdata
.ex_shell
[0] = '\0'; /* for zero length files */
rv
= vn_rdwr(UIO_READ
, ndp
->ni_vp
, (caddr_t
)&exdata
, sizeof(exdata
),
0, UIO_SYSSPACE
, IO_NODELOCKED
, p
->p_ucred
, &amt
, p
);
/* big enough to hold a header? */
if (exdata
.ex_hdr
.a_text
!= 0 && (ndp
->ni_vp
->v_flag
& VTEXT
) == 0 &&
ndp
->ni_vp
->v_writecount
!= 0) {
#define SHELLMAGIC 0x2123 /* #! */
switch (exdata
.ex_hdr
.a_magic
) {
if (exdata
.ex_hdr
.a_text
) {
/* Bill's "screwball mode" */
if ((exdata
.ex_hdr
.a_magic
& 0xffff) != SHELLMAGIC
) {
/* NetBSD compatibility */
switch (ntohl(exdata
.ex_hdr
.a_magic
) & 0xffff) {
for (cp
= &exdata
.ex_shell
[2];; ++cp
) {
if (cp
>= &exdata
.ex_shell
[MAXINTERP
]) {
cp
= &exdata
.ex_shell
[2]; /* get shell interpreter name */
while (*cp
&& *cp
!= ' ')
/* copy the args in the #! line */
indir
= 1; /* indicate this is a script file */
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
ndp
->ni_dirp
= shellname
; /* find shell interpreter */
ndp
->ni_segflg
= UIO_SYSSPACE
;
/* sanity check "ain't not such thing as a sanity clause" -groucho */
if (/*exdata.ex_hdr.a_text == 0 || */ exdata
.ex_hdr
.a_text
> MAXTSIZ
||
exdata
.ex_hdr
.a_text
% NBPG
|| exdata
.ex_hdr
.a_text
> attr
.va_size
)
if (exdata
.ex_hdr
.a_data
== 0 || exdata
.ex_hdr
.a_data
> DFLDSIZ
|| exdata
.ex_hdr
.a_data
> attr
.va_size
|| exdata
.ex_hdr
.a_data
+ exdata
.ex_hdr
.a_text
> attr
.va_size
)
if (exdata
.ex_hdr
.a_bss
> MAXDSIZ
)
if (exdata
.ex_hdr
.a_text
+ exdata
.ex_hdr
.a_data
+ exdata
.ex_hdr
.a_bss
> MAXTSIZ
+ MAXDSIZ
)
if (exdata
.ex_hdr
.a_data
+ exdata
.ex_hdr
.a_bss
> p
->p_rlimit
[RLIMIT_DATA
].rlim_cur
)
if (exdata
.ex_hdr
.a_entry
> exdata
.ex_hdr
.a_text
+ exdata
.ex_hdr
.a_data
)
* Step 3. File and header are valid. Now, dig out the strings
* out of the old process image.
* We implement a single-pass algorithm that builds a new stack
* frame within the address space of the "old" process image,
* avoiding the second pass entirely. Thus, the new frame is
* in position to be run. This consumes much virtual address space,
* and two pages more of 'real' memory, such are the costs.
* [Also, note the cache wipe that's avoided!]
/* create anonymous memory region for new stack */
if ((unsigned)vs
->vm_maxsaddr
+ MAXSSIZ
< USRSTACK
)
newframe
= (caddr_t
) USRSTACK
- MAXSSIZ
;
vs
->vm_maxsaddr
= newframe
= (caddr_t
) USRSTACK
- 2*MAXSSIZ
;
/* don't do stack limit checking on traps temporarily XXX*/
rv
= vm_allocate(&vs
->vm_map
, &newframe
, MAXSSIZ
, FALSE
);
/* allocate string buffer and arg buffer */
argbuf
= (char **) (newframe
+ MAXSSIZ
- 3*ARG_MAX
);
stringbuf
= stringbufp
= ((char *)argbuf
) + 2*ARG_MAX
;
/* first, do (shell name if any then) args */
/* did we outgrow initial argbuf, if so, die */
if (argbufp
>= (char **)stringbuf
) {
if (rv
= copyoutstr(ep
, stringbufp
,
(u_int
)limitonargs
, (u_int
*)&stringlen
)) {
suword(argbufp
++, (int)stringbufp
);
limitonargs
-= stringlen
;
/* orginal executable is 1st argument with scripts */
/* terminate in case no more args to script */
if (vectp
= uap
->argp
) vectp
++; /* manually doing the first
if(vectp
== 0) goto dont_bother
;
/* for each envp, copy in string */
/* did we outgrow initial argbuf, if so, die */
if (argbufp
== (char **)stringbuf
) {
/* get an string pointer */
ep
= (char *)fuword(vectp
++);
/* if not a null pointer, copy string */
if (rv
= copyinoutstr(ep
, stringbufp
,
(u_int
)limitonargs
, (u_int
*) &stringlen
)) {
suword(argbufp
++, (int)stringbufp
);
limitonargs
-= stringlen
;
} while (limitonargs
> 0);
/* have we done the environment yet ? */
/* remember the arg count for later */
/* At this point, one could optionally implement a
* second pass to condense the strings, arguement vectors,
* and stack to fit the fewest pages.
* One might selectively do this when copying was cheaper
* than leaving allocated two more pages per process.
/* stuff arg count on top of "new" stack */
/* argbuf[-1] = (char *)argc;*/
* Step 4. Build the new processes image.
* At this point, we are committed -- destroy old executable!
/* blow away all address space, except the stack */
rv
= vm_deallocate(&vs
->vm_map
, 0, USRSTACK
- 2*MAXSSIZ
);
/* destroy "old" stack */
if ((unsigned)newframe
< USRSTACK
- MAXSSIZ
) {
rv
= vm_deallocate(&vs
->vm_map
, USRSTACK
- MAXSSIZ
, MAXSSIZ
);
rv
= vm_deallocate(&vs
->vm_map
, USRSTACK
- 2*MAXSSIZ
, MAXSSIZ
);
/* build a new address space */
/* treat text, data, and bss in terms of integral page size */
tsize
= roundup(exdata
.ex_hdr
.a_text
, NBPG
);
dsize
= roundup(exdata
.ex_hdr
.a_data
, NBPG
);
bsize
= roundup(exdata
.ex_hdr
.a_bss
, NBPG
);
/* map text as being read/execute only and demand paged */
rv
= vm_mmap(&vs
->vm_map
, &addr
, tsize
, VM_PROT_READ
|VM_PROT_EXECUTE
,
VM_PROT_DEFAULT
, MAP_FILE
|MAP_PRIVATE
|MAP_FIXED
,
(caddr_t
)ndp
->ni_vp
, file_offset
);
addr
= virtual_offset
+ tsize
;
/* map data as being read/write and demand paged */
rv
= vm_mmap(&vs
->vm_map
, &addr
, dsize
,
VM_PROT_READ
| VM_PROT_WRITE
| (tsize
? 0 : VM_PROT_EXECUTE
),
VM_PROT_DEFAULT
, MAP_FILE
|MAP_PRIVATE
|MAP_FIXED
,
(caddr_t
)ndp
->ni_vp
, file_offset
+ tsize
);
/* create anonymous memory region for bss */
addr
= virtual_offset
+ tsize
+ dsize
;
rv
= vm_allocate(&vs
->vm_map
, &addr
, bsize
, FALSE
);
* Step 5. Prepare process for execution.
/* touchup process information -- vm system is unfinished! */
vs
->vm_tsize
= tsize
/NBPG
; /* text size (pages) XXX */
vs
->vm_dsize
= (dsize
+bsize
)/NBPG
; /* data size (pages) XXX */
vs
->vm_taddr
= (caddr_t
) virtual_offset
; /* virtual address of text */
vs
->vm_daddr
= (caddr_t
) virtual_offset
+ tsize
; /* virtual address of data */
vs
->vm_maxsaddr
= newframe
; /* user VA at max stack growth XXX */
vs
->vm_ssize
= ((unsigned)vs
->vm_maxsaddr
+ MAXSSIZ
- (unsigned)argbuf
)/ NBPG
+ 1; /* stack size (pages) */
dostacklimits
= 1; /* allow stack limits to be enforced XXX */
/* 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
)) {
/* implement set userid/groupid */
if ((attr
.va_mode
&VSUID
) && (p
->p_flag
& STRC
) == 0) {
p
->p_ucred
= crcopy(p
->p_ucred
);
p
->p_cred
->p_svuid
= 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_cred
->p_svgid
= p
->p_ucred
->cr_groups
[0] = attr
.va_gid
;
/* setup initial register state */
p
->p_regs
[SP
] = (unsigned) (argbuf
- 1);
setregs(p
, exdata
.ex_hdr
.a_entry
);
ndp
->ni_vp
->v_flag
|= VTEXT
; /* mark vnode pure text */
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
/* if tracing process, pass control back to debugger so breakpoints
can be set before the program "runs" */
p
->p_acflag
&= ~AFORK
; /* remove fork, but no exec flag */
/* remove interim "new" stack frame we were building */
vm_deallocate(&vs
->vm_map
, newframe
, MAXSSIZ
);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
/* sorry, no more process anymore. exit gracefully */
vm_deallocate(&vs
->vm_map
, newframe
, MAXSSIZ
);
FREE(ndp
->ni_pnbuf
, M_NAMEI
);
kexit(p
, W_EXITCODE(0, SIGABRT
));