Commit | Line | Data |
---|---|---|
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 | ||
39 | static 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 */ | |
46 | execve(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 | ||
163 | do_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 | ||
198 | dont_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 | ||
296 | exec_fail: | |
297 | vput(ndp->ni_vp); | |
298 | return(rv); | |
299 | ||
300 | exec_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 | } |