kern_sharedpage.c revision 1549
1/*
2 * Copyright (c) 1993, David Greenman
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by David Greenman
16 * 4. The name of the developer may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 *	$Id: kern_execve.c,v 1.20 1994/03/26 12:24:27 davidg Exp $
32 */
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/signalvar.h>
37#include <sys/resourcevar.h>
38#include <sys/kernel.h>
39#include <sys/mount.h>
40#include <sys/file.h>
41#include <sys/acct.h>
42#include <sys/exec.h>
43#include <sys/imgact.h>
44#include <sys/stat.h>
45#include <sys/wait.h>
46#include <sys/mman.h>
47#include <sys/malloc.h>
48#include <sys/syslog.h>
49
50#include <vm/vm.h>
51#include <vm/vm_kern.h>
52
53#include <machine/reg.h>
54
55int exec_extract_strings __P((struct image_params *));
56int *exec_copyout_strings __P((struct image_params *));
57
58/*
59 * execsw_set is constructed for us by the linker.  Each of the items
60 * is a pointer to a `const struct execsw', hence the double pointer here.
61 */
62extern const struct linker_set execsw_set;
63const struct execsw **execsw = (const struct execsw **)&execsw_set.ls_items[0];
64
65/*
66 * execve() system call.
67 */
68int
69execve(p, uap, retval)
70	struct proc *p;
71	register struct execve_args *uap;
72	int *retval;
73{
74	struct nameidata nd, *ndp;
75	char *stringbase, *stringp;
76	int *stack_base;
77	int error, resid, len, i;
78	struct image_params image_params, *iparams;
79	struct vnode *vnodep;
80	struct vattr attr;
81	char *image_header;
82
83	iparams = &image_params;
84	bzero((caddr_t)iparams, sizeof(struct image_params));
85	image_header = (char *)0;
86
87	/*
88	 * Initialize a few constants in the common area
89	 */
90	iparams->proc = p;
91	iparams->uap = uap;
92	iparams->attr = &attr;
93
94	/*
95	 * Allocate temporary demand zeroed space for argument and
96	 *	environment strings
97	 */
98	error = vm_allocate(kernel_map, (vm_offset_t *)&iparams->stringbase,
99			    ARG_MAX, TRUE);
100	if (error) {
101		log(LOG_WARNING, "execve: failed to allocate string space\n");
102		return (error);
103	}
104
105	if (!iparams->stringbase) {
106		error = ENOMEM;
107		goto exec_fail;
108	}
109	iparams->stringp = iparams->stringbase;
110	iparams->stringspace = ARG_MAX;
111
112	/*
113	 * Translate the file name. namei() returns a vnode pointer
114	 *	in ni_vp amoung other things.
115	 */
116	ndp = &nd;
117	ndp->ni_cnd.cn_nameiop = LOOKUP;
118	ndp->ni_cnd.cn_flags = LOCKLEAF | FOLLOW | SAVENAME;
119	ndp->ni_cnd.cn_proc = curproc;
120	ndp->ni_cnd.cn_cred = curproc->p_cred->pc_ucred;
121	ndp->ni_segflg = UIO_USERSPACE;
122	ndp->ni_dirp = uap->fname;
123
124interpret:
125
126	error = namei(ndp);
127	if (error) {
128		vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
129			      ARG_MAX);
130		goto exec_fail;
131	}
132
133	iparams->vnodep = vnodep = ndp->ni_vp;
134
135	if (vnodep == NULL) {
136		error = ENOEXEC;
137		goto exec_fail_dealloc;
138	}
139
140	/*
141	 * Check file permissions (also 'opens' file)
142	 */
143	error = exec_check_permissions(iparams);
144	if (error)
145		goto exec_fail_dealloc;
146
147	/*
148	 * Map the image header (first page) of the file into
149	 *	kernel address space
150	 */
151	error = vm_mmap(kernel_map,			/* map */
152			(vm_offset_t *)&image_header,	/* address */
153			PAGE_SIZE,			/* size */
154			VM_PROT_READ, 			/* protection */
155			VM_PROT_READ, 			/* max protection */
156			0,	 			/* flags */
157			(caddr_t)vnodep,		/* vnode */
158			0);				/* offset */
159	if (error) {
160		uprintf("mmap failed: %d\n",error);
161		goto exec_fail_dealloc;
162	}
163	iparams->image_header = image_header;
164
165	/*
166	 * Loop through list of image activators, calling each one.
167	 *	If there is no match, the activator returns -1. If there
168	 *	is a match, but there was an error during the activation,
169	 *	the error is returned. Otherwise 0 means success. If the
170	 *	image is interpreted, loop back up and try activating
171	 *	the interpreter.
172	 */
173	for (i = 0; execsw[i]; ++i) {
174		if (execsw[i]->ex_imgact)
175			error = (*execsw[i]->ex_imgact)(iparams);
176		else
177			continue;
178
179		if (error == -1)
180			continue;
181		if (error)
182			goto exec_fail_dealloc;
183		if (iparams->interpreted) {
184			/* free old vnode and name buffer */
185			vput(ndp->ni_vp);
186			FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI);
187			if (vm_deallocate(kernel_map,
188					  (vm_offset_t)image_header, PAGE_SIZE))
189				panic("execve: header dealloc failed (1)");
190
191			/* set new name to that of the interpreter */
192			ndp->ni_segflg = UIO_SYSSPACE;
193			ndp->ni_dirp = iparams->interpreter_name;
194			ndp->ni_cnd.cn_nameiop = LOOKUP;
195			ndp->ni_cnd.cn_flags = LOCKLEAF | FOLLOW | SAVENAME;
196			ndp->ni_cnd.cn_proc = curproc;
197			ndp->ni_cnd.cn_cred = curproc->p_cred->pc_ucred;
198			goto interpret;
199		}
200		break;
201	}
202	/* If we made it through all the activators and none matched, exit. */
203	if (error == -1) {
204		error = ENOEXEC;
205		goto exec_fail_dealloc;
206	}
207
208	/*
209	 * Copy out strings (args and env) and initialize stack base
210	 */
211	stack_base = exec_copyout_strings(iparams);
212	p->p_vmspace->vm_minsaddr = (char *)stack_base;
213
214	/*
215	 * Stuff argument count as first item on stack
216	 */
217	*(--stack_base) = iparams->argc;
218
219	/* close files on exec */
220	fdcloseexec(p);
221
222	/* reset caught signals */
223	execsigs(p);
224
225	/* name this process - nameiexec(p, ndp) */
226	len = min(ndp->ni_cnd.cn_namelen,MAXCOMLEN);
227	bcopy(ndp->ni_cnd.cn_nameptr, p->p_comm, len);
228	p->p_comm[len] = 0;
229
230	/*
231	 * mark as executable, wakeup any process that was vforked and tell
232	 * it that it now has it's own resources back
233	 */
234	p->p_flag |= P_EXEC;
235	if (p->p_pptr && (p->p_flag & P_PPWAIT)) {
236		p->p_flag &= ~P_PPWAIT;
237		wakeup((caddr_t)p->p_pptr);
238	}
239
240	/* implement set userid/groupid */
241	p->p_flag &= ~P_SUGID;
242
243	/*
244	 * Turn off kernel tracing for set-id programs, except for
245	 * root.
246	 */
247	if (p->p_tracep && (attr.va_mode & (VSUID | VSGID)) &&
248	    suser(p->p_ucred, &p->p_acflag)) {
249		p->p_traceflag = 0;
250		vrele(p->p_tracep);
251		p->p_tracep = 0;
252	}
253	if ((attr.va_mode & VSUID) && (p->p_flag & P_TRACED) == 0) {
254		p->p_ucred = crcopy(p->p_ucred);
255		p->p_ucred->cr_uid = attr.va_uid;
256		p->p_flag |= P_SUGID;
257	}
258	if ((attr.va_mode & VSGID) && (p->p_flag & P_TRACED) == 0) {
259		p->p_ucred = crcopy(p->p_ucred);
260		p->p_ucred->cr_groups[0] = attr.va_gid;
261		p->p_flag |= P_SUGID;
262	}
263
264	/*
265	 * Implement correct POSIX saved uid behavior.
266	 */
267	p->p_cred->p_svuid = p->p_ucred->cr_uid;
268	p->p_cred->p_svgid = p->p_ucred->cr_gid;
269
270	/* mark vnode pure text */
271 	ndp->ni_vp->v_flag |= VTEXT;
272
273	/*
274	 * If tracing the process, trap to debugger so breakpoints
275	 * 	can be set before the program executes.
276	 */
277	if (p->p_flag & P_TRACED)
278		psignal(p, SIGTRAP);
279
280	/* clear "fork but no exec" flag, as we _are_ execing */
281	p->p_acflag &= ~AFORK;
282
283	/* Set entry address */
284	setregs(p, iparams->entry_addr, stack_base);
285
286	/*
287	 * free various allocated resources
288	 */
289	if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase, ARG_MAX))
290		panic("execve: string buffer dealloc failed (1)");
291	if (vm_deallocate(kernel_map, (vm_offset_t)image_header, PAGE_SIZE))
292		panic("execve: header dealloc failed (2)");
293	vput(ndp->ni_vp);
294	FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI);
295
296	return (0);
297
298exec_fail_dealloc:
299	if (iparams->stringbase && iparams->stringbase != (char *)-1)
300		if (vm_deallocate(kernel_map, (vm_offset_t)iparams->stringbase,
301				  ARG_MAX))
302			panic("execve: string buffer dealloc failed (2)");
303	if (iparams->image_header && iparams->image_header != (char *)-1)
304		if (vm_deallocate(kernel_map,
305				  (vm_offset_t)iparams->image_header, PAGE_SIZE))
306			panic("execve: header dealloc failed (3)");
307	vput(ndp->ni_vp);
308	FREE(ndp->ni_cnd.cn_pnbuf, M_NAMEI);
309
310exec_fail:
311	if (iparams->vmspace_destroyed) {
312		/* sorry, no more process anymore. exit gracefully */
313#if 0	/* XXX */
314		vm_deallocate(&vs->vm_map, USRSTACK - MAXSSIZ, MAXSSIZ);
315#endif
316		exit1(p, W_EXITCODE(0, SIGABRT));
317		/* NOT REACHED */
318		return(0);
319	} else {
320		return(error);
321	}
322}
323
324/*
325 * Destroy old address space, and allocate a new stack
326 *	The new stack is only SGROWSIZ large because it is grown
327 *	automatically in trap.c.
328 */
329int
330exec_new_vmspace(iparams)
331	struct image_params *iparams;
332{
333	int error;
334	struct vmspace *vmspace = iparams->proc->p_vmspace;
335	caddr_t	stack_addr = (caddr_t) (USRSTACK - SGROWSIZ);
336
337	iparams->vmspace_destroyed = 1;
338
339	/* Blow away entire process VM */
340	vm_deallocate(&vmspace->vm_map, 0, USRSTACK);
341
342	/* Allocate a new stack */
343	error = vm_allocate(&vmspace->vm_map, (vm_offset_t *)&stack_addr,
344			    SGROWSIZ, FALSE);
345	if (error)
346		return(error);
347
348	vmspace->vm_ssize = SGROWSIZ >> PAGE_SHIFT;
349
350	/* Initialize maximum stack address */
351	vmspace->vm_maxsaddr = (char *)USRSTACK - MAXSSIZ;
352
353	return(0);
354}
355
356/*
357 * Copy out argument and environment strings from the old process
358 *	address space into the temporary string buffer.
359 */
360int
361exec_extract_strings(iparams)
362	struct image_params *iparams;
363{
364	char	**argv, **envv;
365	char	*argp, *envp;
366	int	length;
367
368	/*
369	 * extract arguments first
370	 */
371
372	argv = iparams->uap->argv;
373
374	if (argv)
375		while (argp = (caddr_t) fuword(argv++)) {
376			if (argp == (caddr_t) -1)
377				return (EFAULT);
378			if (copyinstr(argp, iparams->stringp, iparams->stringspace,
379				&length) == ENAMETOOLONG)
380					return(E2BIG);
381			iparams->stringspace -= length;
382			iparams->stringp += length;
383			iparams->argc++;
384		}
385
386	/*
387	 * extract environment strings
388	 */
389
390	envv = iparams->uap->envv;
391
392	if (envv)
393		while (envp = (caddr_t) fuword(envv++)) {
394			if (envp == (caddr_t) -1)
395				return (EFAULT);
396			if (copyinstr(envp, iparams->stringp, iparams->stringspace,
397				&length) == ENAMETOOLONG)
398					return(E2BIG);
399			iparams->stringspace -= length;
400			iparams->stringp += length;
401			iparams->envc++;
402		}
403
404	return (0);
405}
406
407/*
408 * Copy strings out to the new process address space, constructing
409 *	new arg and env vector tables. Return a pointer to the base
410 *	so that it can be used as the initial stack pointer.
411 */
412int *
413exec_copyout_strings(iparams)
414	struct image_params *iparams;
415{
416	int argc, envc;
417	char **vectp;
418	char *stringp, *destp;
419	int *stack_base;
420	int vect_table_size, string_table_size;
421
422	/*
423	 * Calculate string base and vector table pointers.
424	 */
425	destp = (caddr_t) ((caddr_t)USRSTACK -
426		roundup((ARG_MAX - iparams->stringspace), sizeof(char *)));
427	/*
428	 * The '+ 2' is for the null pointers at the end of each of the
429	 *	arg and	env vector sets
430	 */
431	vectp = (char **) (destp -
432		(iparams->argc + iparams->envc + 2) * sizeof(char *));
433
434	/*
435	 * vectp also becomes our initial stack base
436	 */
437	stack_base = (int *)vectp;
438
439	stringp = iparams->stringbase;
440	argc = iparams->argc;
441	envc = iparams->envc;
442
443	for (; argc > 0; --argc) {
444		*(vectp++) = destp;
445		while (*destp++ = *stringp++);
446	}
447
448	/* a null vector table pointer seperates the argp's from the envp's */
449	*(vectp++) = NULL;
450
451	for (; envc > 0; --envc) {
452		*(vectp++) = destp;
453		while (*destp++ = *stringp++);
454	}
455
456	/* end of vector table is a null pointer */
457	*vectp = NULL;
458
459	return (stack_base);
460}
461
462/*
463 * Check permissions of file to execute.
464 *	Return 0 for success or error code on failure.
465 */
466int
467exec_check_permissions(iparams)
468	struct image_params *iparams;
469{
470	struct proc *p = iparams->proc;
471	struct vnode *vnodep = iparams->vnodep;
472	struct vattr *attr = iparams->attr;
473	int error;
474
475	/*
476	 * Check number of open-for-writes on the file and deny execution
477	 *	if there are any.
478	 */
479	if (vnodep->v_writecount) {
480		return (ETXTBSY);
481	}
482
483	/* Get file attributes */
484	error = VOP_GETATTR(vnodep, attr, p->p_ucred, p);
485	if (error)
486		return (error);
487
488	/*
489	 * 1) Check if file execution is disabled for the filesystem that this
490	 *	file resides on.
491	 * 2) Insure that at least one execute bit is on - otherwise root
492	 *	will always succeed, and we don't want to happen unless the
493	 *	file really is executable.
494	 * 3) Insure that the file is a regular file.
495	 */
496	if ((vnodep->v_mount->mnt_flag & MNT_NOEXEC) ||
497	    ((attr->va_mode & 0111) == 0) ||
498	    (attr->va_type != VREG)) {
499		return (EACCES);
500	}
501
502	/*
503	 * Zero length files can't be exec'd
504	 */
505	if (attr->va_size == 0)
506		return (ENOEXEC);
507
508	/*
509	 * Disable setuid/setgid if the filesystem prohibits it or if
510	 *	the process is being traced.
511	 */
512        if ((vnodep->v_mount->mnt_flag & MNT_NOSUID) || (p->p_flag & P_TRACED))
513		attr->va_mode &= ~(VSUID | VSGID);
514
515	/*
516	 *  Check for execute permission to file based on current credentials.
517	 *	Then call filesystem specific open routine (which does nothing
518	 *	in the general case).
519	 */
520	error = VOP_ACCESS(vnodep, VEXEC, p->p_ucred, p);
521	if (error)
522		return (error);
523
524	error = VOP_OPEN(vnodep, FREAD, p->p_ucred, p);
525	if (error)
526		return (error);
527
528	return (0);
529}
530