This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / sys / kern / kern_execve
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1992 William Jolitz. All rights reserved.
3 * Written by William Jolitz 1/92
4 *
5 * Redistribution and use in source and binary forms are freely permitted
6 * provided that the above copyright notice and attribution and date of work
7 * and this paragraph are duplicated in all such forms.
8 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * This procedure implements a minimal program execution facility for
13 * 386BSD. It interfaces to the BSD kernel as the execve system call.
14 * Significant limitations and lack of compatiblity with POSIX are
15 * present with this version, to make its basic operation more clear.
16 *
17 */
18
19#include "param.h"
20#include "systm.h"
21#include "proc.h"
22#include "mount.h"
23#include "namei.h"
24#include "vnode.h"
25#include "file.h"
26#include "exec.h"
27#include "stat.h"
28#include "wait.h"
29#include "signalvar.h"
30#include "mman.h"
31
32#include "vm/vm.h"
33#include "vm/vm_param.h"
34#include "vm/vm_map.h"
35#include "vm/vm_kern.h"
36
37#include "machine/reg.h"
38
39static char rcsid[] = "$Header: /usr/bill/working/sys/kern/RCS/kern_execve.c,v 1.3 92/01/21 21:29:13 william Exp $";
40
41/*
42 * Bill's first-cut execve() system call. Puts hair on your chest.
43 */
44
45/* ARGSUSED */
46execve(p, uap, retval)
47 struct proc *p;
48 register struct args {
49 char *fname;
50 char **argp;
51 char **envp;
52 } *uap;
53 int *retval;
54{
55 register struct nameidata *ndp;
56 int rv, amt;
57 struct nameidata nd;
58 struct exec hdr;
59 char **kargbuf, **kargbufp, *kstringbuf, *kstringbufp;
60 char **org, **vectp, *ep;
61 u_int needsenv, limitonargs, stringlen;
62 int addr, size;
63 int argc;
64 char *cp;
65 struct stat statb;
66 struct vmspace *vs;
67 int tsize, dsize, bsize, cnt, foff;
68
69 /*
70 * Step 1. Lookup filename to see if we have something to execute.
71 */
72 ndp = &nd;
73 ndp->ni_nameiop = LOOKUP | LOCKLEAF | FOLLOW;
74 ndp->ni_segflg = UIO_USERSPACE;
75 ndp->ni_dirp = uap->fname;
76
77 /* is it there? */
78 if (rv = namei(ndp, p))
79 return (rv);
80
81 /* is it a regular file? */
82 if (ndp->ni_vp->v_type != VREG) {
83 vput(ndp->ni_vp);
84 return(ENOEXEC);
85 }
86
87 /* is it executable? */
88 rv = VOP_ACCESS(ndp->ni_vp, VEXEC, p->p_ucred, p);
89 if (rv)
90 goto exec_fail;
91
92 rv = vn_stat(ndp->ni_vp, &statb, p);
93 if (rv)
94 goto exec_fail;
95
96 /*
97 * Step 2. Does the file contain a format we can
98 * understand and execute
99 */
100 rv = vn_rdwr(UIO_READ, ndp->ni_vp, (caddr_t)&hdr, sizeof(hdr),
101 0, UIO_SYSSPACE, IO_NODELOCKED, p->p_ucred, &amt, p);
102
103 /* big enough to hold a header? */
104 if (rv)
105 goto exec_fail;
106
107 /* that we recognize? */
108 rv = ENOEXEC;
109 if (hdr.a_magic != ZMAGIC)
110 goto exec_fail;
111
112 /* sanity check "ain't not such thing as a sanity clause" -groucho */
113 if (/*hdr.a_text == 0 || */ hdr.a_text > MAXTSIZ
114 || hdr.a_text % NBPG || hdr.a_text > statb.st_size)
115 goto exec_fail;
116
117 if (hdr.a_data == 0 || hdr.a_data > DFLDSIZ
118 || hdr.a_data > statb.st_size
119 || hdr.a_data + hdr.a_text > statb.st_size)
120 goto exec_fail;
121
122 if (hdr.a_bss > MAXDSIZ)
123 goto exec_fail;
124
125 if (hdr.a_text + hdr.a_data + hdr.a_bss > MAXTSIZ + MAXDSIZ)
126 goto exec_fail;
127
128 /*
129 * Step 3. File and header are valid. Now, dig out the strings
130 * out of the old process image.
131 */
132
133 /* assumption: most execve's have less than 256 arguments, with a
134 * total of string storage space not exceeding 2K. It is more
135 * frequent that when this fails, string space falls short first
136 * (e.g. as when a large termcap environment variable is present).
137 * It is infrequent when more than 256 arguments are used that take
138 * up less than 2K of space (e.g. args average more than 8 chars).
139 *
140 * What we give up in this implementation is a dense encoding of
141 * the data structure in the receiving program's address space.
142 * This means that there is plenty of wasted space (up to 6KB)
143 * as the price we pay for a fast, single pass algorithm.
144 *
145 * Our alternative would be to accumulate strings and pointers
146 * in the first pass, then, knowing the sizes and number of the
147 * strings, pack them neatly and tightly togeither in the second
148 * pass. This means two copies of the strings, and string copying
149 * is much of the cost of exec.
150 */
151
152 /* allocate string buffer and arg buffer */
153 org = kargbuf = (char **) kmem_alloc_wait(exec_map,
154 (NCARGS + PAGE_SIZE)/PAGE_SIZE);
155 kstringbuf = kstringbufp = ((char *)kargbuf) + NBPG/2;
156 kargbuf += NBPG/(4*sizeof(int));
157 kargbufp = kargbuf;
158
159 /* first, do args */
160 needsenv = 1;
161 vectp = uap->argp;
162
163do_env_as_well:
164 cnt = 0;
165 /* for each envp, copy in string */
166 limitonargs = NCARGS;
167 if(vectp == 0) goto dont_bother;
168 do {
169 /* did we outgrow initial argbuf, if so, die */
170 if (kargbufp == (char **)kstringbuf)
171 goto exec_fail;
172
173 /* get an string pointer */
174 ep = (char *)fuword(vectp++);
175 if (ep == (char *)-1) {
176 rv = EFAULT;
177 goto exec_fail;
178 }
179
180 /* if not null pointer, copy in string */
181 if (ep) {
182 if (rv = copyinstr(ep, kstringbufp, limitonargs,
183 &stringlen)) goto exec_fail;
184 /* assume that strings usually all fit in last page */
185 *kargbufp = (char *)(kstringbufp - kstringbuf
186 + USRSTACK - NBPG + NBPG/2);
187 kargbufp++;
188 cnt++;
189 kstringbufp += stringlen;
190 limitonargs -= stringlen + sizeof(long);
191 } else {
192 *kargbufp++ = 0;
193 limitonargs -= sizeof(long);
194 break;
195 }
196 } while (limitonargs > 0);
197
198dont_bother:
199 if (limitonargs <= 0) {
200 rv = E2BIG;
201 goto exec_fail;
202 }
203
204 if (needsenv) {
205 argc = cnt;
206 vectp = uap->envp;
207 needsenv = 0;
208 goto do_env_as_well;
209 }
210
211 kargbuf[-1] = (char *)argc;
212
213 /*
214 * Step 4. Build the new processes image.
215 */
216
217 /* At this point, we are committed -- destroy old executable */
218 vs = p->p_vmspace;
219 addr = 0;
220 size = USRSTACK - addr;
221 /* blow away all address space */
222 rv = vm_deallocate(&vs->vm_map, addr, size, FALSE);
223 if (rv)
224 goto exec_abort;
225
226 /* build a new address space */
227 addr = 0;
228 if (hdr.a_text == 0) {
229 /* screwball mode */
230 foff = tsize = 0;
231 hdr.a_data += hdr.a_text;
232 } else {
233 tsize = roundup(hdr.a_text, NBPG);
234 foff = NBPG;
235 }
236 dsize = roundup(hdr.a_data, NBPG);
237 bsize = roundup(hdr.a_bss + dsize, NBPG);
238 bsize -= dsize;
239
240 /* map text & data*/
241 rv = vm_mmap(&vs->vm_map, &addr, tsize+dsize, VM_PROT_ALL,
242 MAP_FILE|MAP_COPY|MAP_FIXED, (caddr_t)ndp->ni_vp, foff);
243 if (rv)
244 goto exec_abort;
245
246 /* r/w data, ro text */
247 if (tsize) {
248 addr = 0;
249 rv = vm_protect(&vs->vm_map, addr, tsize, FALSE, VM_PROT_READ|VM_PROT_EXECUTE);
250 if (rv)
251 goto exec_abort;
252 }
253
254 /* create anonymous memory region for bss */
255 addr = dsize + tsize;
256 rv = vm_allocate(&vs->vm_map, &addr, bsize, FALSE);
257 if (rv)
258 goto exec_abort;
259
260 /* create anonymous memory region for stack */
261 addr = USRSTACK - MAXSSIZ;
262 rv = vm_allocate(&vs->vm_map, &addr, MAXSSIZ, FALSE);
263 if (rv)
264 goto exec_abort;
265
266 /*
267 * Step 5. Prepare process for execution.
268 */
269
270 /* touchup process information */
271 vs->vm_tsize = tsize/NBPG; /* text size (pages) XXX */
272 vs->vm_dsize = (dsize+bsize)/NBPG; /* data size (pages) XXX */
273 vs->vm_ssize = MAXSSIZ/NBPG; /* stack size (pages) */
274 vs->vm_taddr = 0; /* user virtual address of text XXX */
275 vs->vm_daddr = (caddr_t)tsize; /* user virtual address of data XXX */
276 /* user VA at max stack growth */
277 vs->vm_maxsaddr = (caddr_t)(USRSTACK - MAXSSIZ);
278
279 /* everything fits in a single page, no fixups, no more work */
280 /* (groan) due to bug in vm_map_copy, can't remap. copy for now. */
281 rv = copyout((caddr_t)org, (caddr_t)USRSTACK - NBPG, NBPG);
282 if(rv)
283 goto exec_abort;
284
285 /* close files on exec, fixup signals */
286 fdcloseexec(p);
287 execsigs(p);
288
289 p->p_regs[SP] = USRSTACK - NBPG + NBPG/4 - 4;
290 vs->vm_ssize = 1; /* stack size (pages) */
291 setregs(p, hdr.a_entry);
292 kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE);
293 vput(ndp->ni_vp);
294 return (0);
295
296exec_fail:
297 vput(ndp->ni_vp);
298 return(rv);
299
300exec_abort:
301 /* untested and probably bogus */
302 kmem_free_wakeup(exec_map, org, (NCARGS + PAGE_SIZE)/PAGE_SIZE);
303 vput(ndp->ni_vp);
304 exit(p, W_EXITCODE(0, SIGABRT));
305 return(0);
306
307}