xref: /freebsd/sys/kern/imgact_binmisc.c (revision 5eeb4f737f11b253ac330ae459b05e30fd16d0e8)
16d756449SSean Bruno /*-
2910938f0SSean Bruno  * Copyright (c) 2013-16, Stacey D. Son
36d756449SSean Bruno  * All rights reserved.
46d756449SSean Bruno  *
56d756449SSean Bruno  * Redistribution and use in source and binary forms, with or without
66d756449SSean Bruno  * modification, are permitted provided that the following conditions
76d756449SSean Bruno  * are met:
86d756449SSean Bruno  * 1. Redistributions of source code must retain the above copyright
96d756449SSean Bruno  *    notice, this list of conditions and the following disclaimer.
106d756449SSean Bruno  * 2. Redistributions in binary form must reproduce the above copyright
116d756449SSean Bruno  *    notice, this list of conditions and the following disclaimer in the
126d756449SSean Bruno  *    documentation and/or other materials provided with the distribution.
136d756449SSean Bruno  *
146d756449SSean Bruno  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
156d756449SSean Bruno  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
166d756449SSean Bruno  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
176d756449SSean Bruno  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
186d756449SSean Bruno  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
196d756449SSean Bruno  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
206d756449SSean Bruno  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
216d756449SSean Bruno  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
226d756449SSean Bruno  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
236d756449SSean Bruno  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
246d756449SSean Bruno  * SUCH DAMAGE.
256d756449SSean Bruno  */
266d756449SSean Bruno 
276d756449SSean Bruno #include <sys/cdefs.h>
286d756449SSean Bruno __FBSDID("$FreeBSD$");
296d756449SSean Bruno 
306d756449SSean Bruno #include <sys/param.h>
316d756449SSean Bruno #include <sys/ctype.h>
326d756449SSean Bruno #include <sys/exec.h>
33*5eeb4f73SDoug Rabson #include <sys/fcntl.h>
346d756449SSean Bruno #include <sys/imgact.h>
356d756449SSean Bruno #include <sys/imgact_binmisc.h>
366d756449SSean Bruno #include <sys/kernel.h>
376d756449SSean Bruno #include <sys/lock.h>
386d756449SSean Bruno #include <sys/malloc.h>
396d756449SSean Bruno #include <sys/mutex.h>
40*5eeb4f73SDoug Rabson #include <sys/namei.h>
41df69035dSKyle Evans #include <sys/sbuf.h>
426d756449SSean Bruno #include <sys/sysctl.h>
435f98711dSSean Bruno #include <sys/sx.h>
44*5eeb4f73SDoug Rabson #include <sys/vnode.h>
455f98711dSSean Bruno 
465f98711dSSean Bruno #include <machine/atomic.h>
476d756449SSean Bruno 
486d756449SSean Bruno /**
496d756449SSean Bruno  * Miscellaneous binary interpreter image activator.
506d756449SSean Bruno  *
516d756449SSean Bruno  * If the given target executable's header matches 'xbe_magic' field in the
526d756449SSean Bruno  * 'interpreter_list' then it will use the user-level interpreter specified in
536d756449SSean Bruno  * the 'xbe_interpreter' field to execute the binary. The 'xbe_magic' field may
546d756449SSean Bruno  * be adjusted to a given offset using the value in the 'xbe_moffset' field
556d756449SSean Bruno  * and bits of the header may be masked using the 'xbe_mask' field.  The
566d756449SSean Bruno  * 'interpreter_list' entries are managed using sysctl(3) as described in the
576d756449SSean Bruno  * <sys/imgact_binmisc.h> file.
586d756449SSean Bruno  */
596d756449SSean Bruno 
606d756449SSean Bruno /*
616d756449SSean Bruno  * Node of the interpreter list.
626d756449SSean Bruno  */
636d756449SSean Bruno typedef struct imgact_binmisc_entry {
64ecb4fdf9SKyle Evans 	SLIST_ENTRY(imgact_binmisc_entry) link;
656d756449SSean Bruno 	char				 *ibe_name;
666d756449SSean Bruno 	uint8_t				 *ibe_magic;
676d756449SSean Bruno 	uint8_t				 *ibe_mask;
686d756449SSean Bruno 	uint8_t				 *ibe_interpreter;
69*5eeb4f73SDoug Rabson 	struct vnode			 *ibe_interpreter_vnode;
701024ef27SKyle Evans 	ssize_t				  ibe_interp_offset;
716d756449SSean Bruno 	uint32_t			  ibe_interp_argcnt;
726d756449SSean Bruno 	uint32_t			  ibe_interp_length;
731024ef27SKyle Evans 	uint32_t			  ibe_argv0_cnt;
746d756449SSean Bruno 	uint32_t			  ibe_flags;
75ecb4fdf9SKyle Evans 	uint32_t			  ibe_moffset;
76ecb4fdf9SKyle Evans 	uint32_t			  ibe_msize;
776d756449SSean Bruno } imgact_binmisc_entry_t;
786d756449SSean Bruno 
796d756449SSean Bruno /*
806d756449SSean Bruno  * sysctl() commands.
816d756449SSean Bruno  */
826d756449SSean Bruno #define IBC_ADD		1	/* Add given entry. */
836d756449SSean Bruno #define IBC_REMOVE	2	/* Remove entry for a given name. */
846d756449SSean Bruno #define IBC_DISABLE	3	/* Disable entry for a given name. */
856d756449SSean Bruno #define IBC_ENABLE	4	/* Enable entry for a given name. */
866d756449SSean Bruno #define IBC_LOOKUP	5	/* Lookup and return entry for given name. */
876d756449SSean Bruno #define IBC_LIST	6	/* Get a snapshot of the interpretor list. */
886d756449SSean Bruno 
896d756449SSean Bruno /*
906d756449SSean Bruno  * Interpreter string macros.
916d756449SSean Bruno  *
926d756449SSean Bruno  * They all start with '#' followed by a single letter:
936d756449SSean Bruno  */
946d756449SSean Bruno #define	ISM_POUND	'#'	/* "##" is the escape sequence for single #. */
956d756449SSean Bruno #define	ISM_OLD_ARGV0	'a'	/* "#a" is replaced with the old argv0. */
966d756449SSean Bruno 
976d756449SSean Bruno MALLOC_DEFINE(M_BINMISC, KMOD_NAME, "misc binary image activator");
986d756449SSean Bruno 
996d756449SSean Bruno /* The interpreter list. */
1006d756449SSean Bruno static SLIST_HEAD(, imgact_binmisc_entry) interpreter_list =
1016d756449SSean Bruno 	SLIST_HEAD_INITIALIZER(interpreter_list);
1026d756449SSean Bruno 
103df69035dSKyle Evans static int interp_list_entry_count;
104280b7169SSean Bruno 
1055f98711dSSean Bruno static struct sx interp_list_sx;
1066d756449SSean Bruno 
1072192cd12SKyle Evans #define	INTERP_LIST_WLOCK()		sx_xlock(&interp_list_sx)
1082192cd12SKyle Evans #define	INTERP_LIST_RLOCK()		sx_slock(&interp_list_sx)
1092192cd12SKyle Evans #define	INTERP_LIST_WUNLOCK()		sx_xunlock(&interp_list_sx)
1102192cd12SKyle Evans #define	INTERP_LIST_RUNLOCK()		sx_sunlock(&interp_list_sx)
1112192cd12SKyle Evans 
1122192cd12SKyle Evans #define	INTERP_LIST_LOCK_INIT()		sx_init(&interp_list_sx, KMOD_NAME)
1132192cd12SKyle Evans #define	INTERP_LIST_LOCK_DESTROY()	sx_destroy(&interp_list_sx)
1142192cd12SKyle Evans 
1152192cd12SKyle Evans #define	INTERP_LIST_ASSERT_LOCKED()	sx_assert(&interp_list_sx, SA_LOCKED)
1162192cd12SKyle Evans 
1176d756449SSean Bruno /*
1186d756449SSean Bruno  * Populate the entry with the information about the interpreter.
1196d756449SSean Bruno  */
1206d756449SSean Bruno static void
121*5eeb4f73SDoug Rabson imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe, int flags)
1226d756449SSean Bruno {
1236d756449SSean Bruno 	uint32_t len = 0, argc = 1;
1246d756449SSean Bruno 	char t[IBE_INTERP_LEN_MAX];
1256d756449SSean Bruno 	char *sp, *tp;
1266d756449SSean Bruno 
1275f98711dSSean Bruno 	memset(t, 0, sizeof(t));
1286d756449SSean Bruno 
1296d756449SSean Bruno 	/*
1306d756449SSean Bruno 	 * Normalize interpreter string. Replace white space between args with
1316d756449SSean Bruno 	 * single space.
1326d756449SSean Bruno 	 */
1336d756449SSean Bruno 	sp = str; tp = t;
1346d756449SSean Bruno 	while (*sp != '\0') {
1356d756449SSean Bruno 		if (*sp == ' ' || *sp == '\t') {
13626af6115SEd Maste 			if (++len >= IBE_INTERP_LEN_MAX)
1376d756449SSean Bruno 				break;
1386d756449SSean Bruno 			*tp++ = ' ';
1396d756449SSean Bruno 			argc++;
1406d756449SSean Bruno 			while (*sp == ' ' || *sp == '\t')
1416d756449SSean Bruno 				sp++;
1426d756449SSean Bruno 			continue;
1436d756449SSean Bruno 		} else {
1446d756449SSean Bruno 			*tp++ = *sp++;
1456d756449SSean Bruno 			len++;
1466d756449SSean Bruno 		}
1476d756449SSean Bruno 	}
1486d756449SSean Bruno 	*tp = '\0';
1496d756449SSean Bruno 	len++;
1506d756449SSean Bruno 
1516d756449SSean Bruno 	ibe->ibe_interpreter = malloc(len, M_BINMISC, M_WAITOK|M_ZERO);
1526d756449SSean Bruno 
1536d756449SSean Bruno 	/* Populate all the ibe fields for the interpreter. */
1546d756449SSean Bruno 	memcpy(ibe->ibe_interpreter, t, len);
1556d756449SSean Bruno 	ibe->ibe_interp_argcnt = argc;
1566d756449SSean Bruno 	ibe->ibe_interp_length = len;
157*5eeb4f73SDoug Rabson 
158*5eeb4f73SDoug Rabson 	ibe->ibe_interpreter_vnode = NULL;
159*5eeb4f73SDoug Rabson 	if (flags & IBF_PRE_OPEN) {
160*5eeb4f73SDoug Rabson 		struct nameidata nd;
161*5eeb4f73SDoug Rabson 		int error;
162*5eeb4f73SDoug Rabson 
163*5eeb4f73SDoug Rabson 		tp = t;
164*5eeb4f73SDoug Rabson 		while (*tp != '\0' && *tp != ' ') {
165*5eeb4f73SDoug Rabson 			tp++;
166*5eeb4f73SDoug Rabson 		}
167*5eeb4f73SDoug Rabson 		*tp = '\0';
168*5eeb4f73SDoug Rabson 		NDINIT(&nd, LOOKUP, FOLLOW | ISOPEN, UIO_SYSSPACE, t);
169*5eeb4f73SDoug Rabson 
170*5eeb4f73SDoug Rabson 		/*
171*5eeb4f73SDoug Rabson 		 * If there is an error, just stop now and fall back
172*5eeb4f73SDoug Rabson 		 * to the non pre-open case where we lookup during
173*5eeb4f73SDoug Rabson 		 * exec.
174*5eeb4f73SDoug Rabson 		 */
175*5eeb4f73SDoug Rabson 		error = namei(&nd);
176*5eeb4f73SDoug Rabson 		if (error)
177*5eeb4f73SDoug Rabson 			return;
178*5eeb4f73SDoug Rabson 
179*5eeb4f73SDoug Rabson 		ibe->ibe_interpreter_vnode = nd.ni_vp;
180*5eeb4f73SDoug Rabson 	}
1816d756449SSean Bruno }
1826d756449SSean Bruno 
1836d756449SSean Bruno /*
1846d756449SSean Bruno  * Allocate memory and populate a new entry for the interpreter table.
1856d756449SSean Bruno  */
1866d756449SSean Bruno static imgact_binmisc_entry_t *
1871024ef27SKyle Evans imgact_binmisc_new_entry(ximgact_binmisc_entry_t *xbe, ssize_t interp_offset,
1881024ef27SKyle Evans     int argv0_cnt)
1896d756449SSean Bruno {
1906d756449SSean Bruno 	imgact_binmisc_entry_t *ibe = NULL;
1916d756449SSean Bruno 	size_t namesz = min(strlen(xbe->xbe_name) + 1, IBE_NAME_MAX);
1926d756449SSean Bruno 
1936d756449SSean Bruno 	ibe = malloc(sizeof(*ibe), M_BINMISC, M_WAITOK|M_ZERO);
1946d756449SSean Bruno 
1956d756449SSean Bruno 	ibe->ibe_name = malloc(namesz, M_BINMISC, M_WAITOK|M_ZERO);
1966d756449SSean Bruno 	strlcpy(ibe->ibe_name, xbe->xbe_name, namesz);
1976d756449SSean Bruno 
198*5eeb4f73SDoug Rabson 	imgact_binmisc_populate_interp(xbe->xbe_interpreter, ibe, xbe->xbe_flags);
1996d756449SSean Bruno 
2006d756449SSean Bruno 	ibe->ibe_magic = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO);
2016d756449SSean Bruno 	memcpy(ibe->ibe_magic, xbe->xbe_magic, xbe->xbe_msize);
2026d756449SSean Bruno 
2036d756449SSean Bruno 	ibe->ibe_mask = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO);
2046d756449SSean Bruno 	memcpy(ibe->ibe_mask, xbe->xbe_mask, xbe->xbe_msize);
2056d756449SSean Bruno 
2066d756449SSean Bruno 	ibe->ibe_moffset = xbe->xbe_moffset;
2076d756449SSean Bruno 	ibe->ibe_msize = xbe->xbe_msize;
2086d756449SSean Bruno 	ibe->ibe_flags = xbe->xbe_flags;
2091024ef27SKyle Evans 	ibe->ibe_interp_offset = interp_offset;
2101024ef27SKyle Evans 	ibe->ibe_argv0_cnt = argv0_cnt;
2116d756449SSean Bruno 	return (ibe);
2126d756449SSean Bruno }
2136d756449SSean Bruno 
2146d756449SSean Bruno /*
2156d756449SSean Bruno  * Free the allocated memory for a given list item.
2166d756449SSean Bruno  */
2176d756449SSean Bruno static void
2186d756449SSean Bruno imgact_binmisc_destroy_entry(imgact_binmisc_entry_t *ibe)
2196d756449SSean Bruno {
2206d756449SSean Bruno 	if (!ibe)
2216d756449SSean Bruno 		return;
222b888dae4SSean Bruno 	if (ibe->ibe_magic)
2236d756449SSean Bruno 		free(ibe->ibe_magic, M_BINMISC);
2246d756449SSean Bruno 	if (ibe->ibe_mask)
2256d756449SSean Bruno 		free(ibe->ibe_mask, M_BINMISC);
2266d756449SSean Bruno 	if (ibe->ibe_interpreter)
2276d756449SSean Bruno 		free(ibe->ibe_interpreter, M_BINMISC);
2286d756449SSean Bruno 	if (ibe->ibe_name)
2296d756449SSean Bruno 		free(ibe->ibe_name, M_BINMISC);
230*5eeb4f73SDoug Rabson 	if (ibe->ibe_interpreter_vnode)
231*5eeb4f73SDoug Rabson 		vrele(ibe->ibe_interpreter_vnode);
2326d756449SSean Bruno 	if (ibe)
2336d756449SSean Bruno 		free(ibe, M_BINMISC);
2346d756449SSean Bruno }
2356d756449SSean Bruno 
2366d756449SSean Bruno /*
2376d756449SSean Bruno  * Find the interpreter in the list by the given name.  Return NULL if not
2386d756449SSean Bruno  * found.
2396d756449SSean Bruno  */
2406d756449SSean Bruno static imgact_binmisc_entry_t *
2416d756449SSean Bruno imgact_binmisc_find_entry(char *name)
2426d756449SSean Bruno {
2436d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
2446d756449SSean Bruno 
2452192cd12SKyle Evans 	INTERP_LIST_ASSERT_LOCKED();
2466d756449SSean Bruno 
2476d756449SSean Bruno 	SLIST_FOREACH(ibe, &interpreter_list, link) {
2486d756449SSean Bruno 		if (strncmp(name, ibe->ibe_name, IBE_NAME_MAX) == 0)
2496d756449SSean Bruno 			return (ibe);
2506d756449SSean Bruno 	}
2516d756449SSean Bruno 
2526d756449SSean Bruno 	return (NULL);
2536d756449SSean Bruno }
2546d756449SSean Bruno 
2556d756449SSean Bruno /*
2566d756449SSean Bruno  * Add the given interpreter if it doesn't already exist.  Return EEXIST
2576d756449SSean Bruno  * if the name already exist in the interpreter list.
2586d756449SSean Bruno  */
2596d756449SSean Bruno static int
2606d756449SSean Bruno imgact_binmisc_add_entry(ximgact_binmisc_entry_t *xbe)
2616d756449SSean Bruno {
2626d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
2636d756449SSean Bruno 	char *p;
2641024ef27SKyle Evans 	ssize_t interp_offset;
2651024ef27SKyle Evans 	int argv0_cnt, cnt;
2666d756449SSean Bruno 
2676d756449SSean Bruno 	if (xbe->xbe_msize > IBE_MAGIC_MAX)
2686d756449SSean Bruno 		return (EINVAL);
2698c28aa5eSKyle Evans 	if (xbe->xbe_moffset + xbe->xbe_msize > IBE_MATCH_MAX)
2708c28aa5eSKyle Evans 		return (EINVAL);
2716d756449SSean Bruno 
272910938f0SSean Bruno 	for(cnt = 0, p = xbe->xbe_name; *p != 0; cnt++, p++)
273910938f0SSean Bruno 		if (cnt >= IBE_NAME_MAX || !isascii((int)*p))
2746d756449SSean Bruno 			return (EINVAL);
2756d756449SSean Bruno 
276910938f0SSean Bruno 	for(cnt = 0, p = xbe->xbe_interpreter; *p != 0; cnt++, p++)
277910938f0SSean Bruno 		if (cnt >= IBE_INTERP_LEN_MAX || !isascii((int)*p))
2786d756449SSean Bruno 			return (EINVAL);
2796d756449SSean Bruno 
2806d756449SSean Bruno 	/* Make sure we don't have any invalid #'s. */
2816d756449SSean Bruno 	p = xbe->xbe_interpreter;
2821024ef27SKyle Evans 	interp_offset = 0;
2831024ef27SKyle Evans 	argv0_cnt = 0;
2841024ef27SKyle Evans 	while ((p = strchr(p, '#')) != NULL) {
2856d756449SSean Bruno 		p++;
2866d756449SSean Bruno 		switch(*p) {
2876d756449SSean Bruno 		case ISM_POUND:
2886d756449SSean Bruno 			/* "##" */
2896d756449SSean Bruno 			p++;
2901024ef27SKyle Evans 			interp_offset--;
2916d756449SSean Bruno 			break;
2926d756449SSean Bruno 		case ISM_OLD_ARGV0:
2936d756449SSean Bruno 			/* "#a" */
2946d756449SSean Bruno 			p++;
2951024ef27SKyle Evans 			argv0_cnt++;
2966d756449SSean Bruno 			break;
2976d756449SSean Bruno 		case 0:
2986d756449SSean Bruno 		default:
2996d756449SSean Bruno 			/* Anything besides the above is invalid. */
3006d756449SSean Bruno 			return (EINVAL);
3016d756449SSean Bruno 		}
3026d756449SSean Bruno 	}
3036d756449SSean Bruno 
304*5eeb4f73SDoug Rabson 	/*
305*5eeb4f73SDoug Rabson 	 * Preallocate a new entry. We do this without holding the
306*5eeb4f73SDoug Rabson 	 * lock to avoid lock-order problems if IBF_PRE_OPEN is
307*5eeb4f73SDoug Rabson 	 * set.
308*5eeb4f73SDoug Rabson 	 */
309*5eeb4f73SDoug Rabson 	ibe = imgact_binmisc_new_entry(xbe, interp_offset, argv0_cnt);
310*5eeb4f73SDoug Rabson 
3112192cd12SKyle Evans 	INTERP_LIST_WLOCK();
312280b7169SSean Bruno 	if (imgact_binmisc_find_entry(xbe->xbe_name) != NULL) {
3132192cd12SKyle Evans 		INTERP_LIST_WUNLOCK();
314*5eeb4f73SDoug Rabson 		imgact_binmisc_destroy_entry(ibe);
315280b7169SSean Bruno 		return (EEXIST);
316280b7169SSean Bruno 	}
317280b7169SSean Bruno 
3186d756449SSean Bruno 	SLIST_INSERT_HEAD(&interpreter_list, ibe, link);
3196d756449SSean Bruno 	interp_list_entry_count++;
3202192cd12SKyle Evans 	INTERP_LIST_WUNLOCK();
3216d756449SSean Bruno 
3226d756449SSean Bruno 	return (0);
3236d756449SSean Bruno }
3246d756449SSean Bruno 
3256d756449SSean Bruno /*
3266d756449SSean Bruno  * Remove the interpreter in the list with the given name. Return ENOENT
3276d756449SSean Bruno  * if not found.
3286d756449SSean Bruno  */
3296d756449SSean Bruno static int
3306d756449SSean Bruno imgact_binmisc_remove_entry(char *name)
3316d756449SSean Bruno {
3326d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
3336d756449SSean Bruno 
3342192cd12SKyle Evans 	INTERP_LIST_WLOCK();
3356d756449SSean Bruno 	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
3362192cd12SKyle Evans 		INTERP_LIST_WUNLOCK();
3376d756449SSean Bruno 		return (ENOENT);
3386d756449SSean Bruno 	}
3396d756449SSean Bruno 	SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry, link);
3406d756449SSean Bruno 	interp_list_entry_count--;
3412192cd12SKyle Evans 	INTERP_LIST_WUNLOCK();
3426d756449SSean Bruno 
3436d756449SSean Bruno 	imgact_binmisc_destroy_entry(ibe);
3446d756449SSean Bruno 
3456d756449SSean Bruno 	return (0);
3466d756449SSean Bruno }
3476d756449SSean Bruno 
3486d756449SSean Bruno /*
3496d756449SSean Bruno  * Disable the interpreter in the list with the given name. Return ENOENT
3506d756449SSean Bruno  * if not found.
3516d756449SSean Bruno  */
3526d756449SSean Bruno static int
3536d756449SSean Bruno imgact_binmisc_disable_entry(char *name)
3546d756449SSean Bruno {
3556d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
3566d756449SSean Bruno 
3572192cd12SKyle Evans 	INTERP_LIST_WLOCK();
3586d756449SSean Bruno 	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
3592192cd12SKyle Evans 		INTERP_LIST_WUNLOCK();
3606d756449SSean Bruno 		return (ENOENT);
3616d756449SSean Bruno 	}
3626d756449SSean Bruno 
3634e83b32aSSean Bruno 	ibe->ibe_flags &= ~IBF_ENABLED;
3642192cd12SKyle Evans 	INTERP_LIST_WUNLOCK();
3656d756449SSean Bruno 
3666d756449SSean Bruno 	return (0);
3676d756449SSean Bruno }
3686d756449SSean Bruno 
3696d756449SSean Bruno /*
3706d756449SSean Bruno  * Enable the interpreter in the list with the given name. Return ENOENT
3716d756449SSean Bruno  * if not found.
3726d756449SSean Bruno  */
3736d756449SSean Bruno static int
3746d756449SSean Bruno imgact_binmisc_enable_entry(char *name)
3756d756449SSean Bruno {
3766d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
3776d756449SSean Bruno 
3782192cd12SKyle Evans 	INTERP_LIST_WLOCK();
3796d756449SSean Bruno 	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
3802192cd12SKyle Evans 		INTERP_LIST_WUNLOCK();
3816d756449SSean Bruno 		return (ENOENT);
3826d756449SSean Bruno 	}
3836d756449SSean Bruno 
3844e83b32aSSean Bruno 	ibe->ibe_flags |= IBF_ENABLED;
3852192cd12SKyle Evans 	INTERP_LIST_WUNLOCK();
3866d756449SSean Bruno 
3876d756449SSean Bruno 	return (0);
3886d756449SSean Bruno }
3896d756449SSean Bruno 
3906d756449SSean Bruno static int
3916d756449SSean Bruno imgact_binmisc_populate_xbe(ximgact_binmisc_entry_t *xbe,
3926d756449SSean Bruno     imgact_binmisc_entry_t *ibe)
3936d756449SSean Bruno {
3946d756449SSean Bruno 	uint32_t i;
3956d756449SSean Bruno 
3962192cd12SKyle Evans 	INTERP_LIST_ASSERT_LOCKED();
3975f98711dSSean Bruno 	memset(xbe, 0, sizeof(*xbe));
3986d756449SSean Bruno 	strlcpy(xbe->xbe_name, ibe->ibe_name, IBE_NAME_MAX);
3996d756449SSean Bruno 
4006d756449SSean Bruno 	/* Copy interpreter string.  Replace NULL breaks with space. */
4016d756449SSean Bruno 	memcpy(xbe->xbe_interpreter, ibe->ibe_interpreter,
4026d756449SSean Bruno 	    ibe->ibe_interp_length);
4036d756449SSean Bruno 	for(i = 0; i < (ibe->ibe_interp_length - 1); i++)
4046d756449SSean Bruno 		if (xbe->xbe_interpreter[i] == '\0')
4056d756449SSean Bruno 			xbe->xbe_interpreter[i] = ' ';
4066d756449SSean Bruno 
4076d756449SSean Bruno 	memcpy(xbe->xbe_magic, ibe->ibe_magic, ibe->ibe_msize);
4086d756449SSean Bruno 	memcpy(xbe->xbe_mask, ibe->ibe_mask, ibe->ibe_msize);
4096d756449SSean Bruno 	xbe->xbe_version = IBE_VERSION;
4106d756449SSean Bruno 	xbe->xbe_flags = ibe->ibe_flags;
4116d756449SSean Bruno 	xbe->xbe_moffset = ibe->ibe_moffset;
4126d756449SSean Bruno 	xbe->xbe_msize = ibe->ibe_msize;
4136d756449SSean Bruno 
4146d756449SSean Bruno 	return (0);
4156d756449SSean Bruno }
4166d756449SSean Bruno 
4176d756449SSean Bruno /*
4186d756449SSean Bruno  * Retrieve the interpreter with the give name and populate the
4196d756449SSean Bruno  * ximgact_binmisc_entry structure.  Return ENOENT if not found.
4206d756449SSean Bruno  */
4216d756449SSean Bruno static int
4226d756449SSean Bruno imgact_binmisc_lookup_entry(char *name, ximgact_binmisc_entry_t *xbe)
4236d756449SSean Bruno {
4246d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
4256d756449SSean Bruno 	int error = 0;
4266d756449SSean Bruno 
4272192cd12SKyle Evans 	INTERP_LIST_RLOCK();
4286d756449SSean Bruno 	if ((ibe = imgact_binmisc_find_entry(name)) == NULL) {
4292192cd12SKyle Evans 		INTERP_LIST_RUNLOCK();
4306d756449SSean Bruno 		return (ENOENT);
4316d756449SSean Bruno 	}
4326d756449SSean Bruno 
4336d756449SSean Bruno 	error = imgact_binmisc_populate_xbe(xbe, ibe);
4342192cd12SKyle Evans 	INTERP_LIST_RUNLOCK();
4356d756449SSean Bruno 
4366d756449SSean Bruno 	return (error);
4376d756449SSean Bruno }
4386d756449SSean Bruno 
4396d756449SSean Bruno /*
4406d756449SSean Bruno  * Get a snapshot of all the interpreter entries in the list.
4416d756449SSean Bruno  */
4426d756449SSean Bruno static int
4436d756449SSean Bruno imgact_binmisc_get_all_entries(struct sysctl_req *req)
4446d756449SSean Bruno {
4456d756449SSean Bruno 	ximgact_binmisc_entry_t *xbe, *xbep;
4466d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
4476d756449SSean Bruno 	int error = 0, count;
4486d756449SSean Bruno 
4492192cd12SKyle Evans 	INTERP_LIST_RLOCK();
4506d756449SSean Bruno 	count = interp_list_entry_count;
451e0ae213fSSean Bruno 	xbe = malloc(sizeof(*xbe) * count, M_BINMISC, M_WAITOK|M_ZERO);
4526d756449SSean Bruno 
4536d756449SSean Bruno 	xbep = xbe;
4546d756449SSean Bruno 	SLIST_FOREACH(ibe, &interpreter_list, link) {
4556d756449SSean Bruno 		error = imgact_binmisc_populate_xbe(xbep++, ibe);
4566d756449SSean Bruno 		if (error)
4576d756449SSean Bruno 			break;
4586d756449SSean Bruno 	}
4592192cd12SKyle Evans 	INTERP_LIST_RUNLOCK();
4606d756449SSean Bruno 
4616d756449SSean Bruno 	if (!error)
4626d756449SSean Bruno 		error = SYSCTL_OUT(req, xbe, sizeof(*xbe) * count);
4636d756449SSean Bruno 
4646d756449SSean Bruno 	free(xbe, M_BINMISC);
4656d756449SSean Bruno 	return (error);
4666d756449SSean Bruno }
4676d756449SSean Bruno 
4686d756449SSean Bruno /*
4696d756449SSean Bruno  * sysctl() handler for munipulating interpretor table.
4706d756449SSean Bruno  * Not MP safe (locked by sysctl).
4716d756449SSean Bruno  */
4726d756449SSean Bruno static int
4736d756449SSean Bruno sysctl_kern_binmisc(SYSCTL_HANDLER_ARGS)
4746d756449SSean Bruno {
4756d756449SSean Bruno 	ximgact_binmisc_entry_t xbe;
4766d756449SSean Bruno 	int error = 0;
4776d756449SSean Bruno 
4786d756449SSean Bruno 	switch(arg2) {
4796d756449SSean Bruno 	case IBC_ADD:
4806d756449SSean Bruno 		/* Add an entry. Limited to IBE_MAX_ENTRIES. */
4816d756449SSean Bruno 		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
4826d756449SSean Bruno 		if (error)
4836d756449SSean Bruno 			return (error);
4846d756449SSean Bruno 		if (IBE_VERSION != xbe.xbe_version)
4856d756449SSean Bruno 			return (EINVAL);
4867d3ed977SKyle Evans 		if ((xbe.xbe_flags & ~IBF_VALID_UFLAGS) != 0)
4877d3ed977SKyle Evans 			return (EINVAL);
4886d756449SSean Bruno 		if (interp_list_entry_count == IBE_MAX_ENTRIES)
4896d756449SSean Bruno 			return (ENOSPC);
4906d756449SSean Bruno 		error = imgact_binmisc_add_entry(&xbe);
4916d756449SSean Bruno 		break;
4926d756449SSean Bruno 
4936d756449SSean Bruno 	case IBC_REMOVE:
4946d756449SSean Bruno 		/* Remove an entry. */
4956d756449SSean Bruno 		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
4966d756449SSean Bruno 		if (error)
4976d756449SSean Bruno 			return (error);
4986d756449SSean Bruno 		if (IBE_VERSION != xbe.xbe_version)
4996d756449SSean Bruno 			return (EINVAL);
5006d756449SSean Bruno 		error = imgact_binmisc_remove_entry(xbe.xbe_name);
5016d756449SSean Bruno 		break;
5026d756449SSean Bruno 
5036d756449SSean Bruno 	case IBC_DISABLE:
5046d756449SSean Bruno 		/* Disable an entry. */
5056d756449SSean Bruno 		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
5066d756449SSean Bruno 		if (error)
5076d756449SSean Bruno 			return (error);
5086d756449SSean Bruno 		if (IBE_VERSION != xbe.xbe_version)
5096d756449SSean Bruno 			return (EINVAL);
5106d756449SSean Bruno 		error = imgact_binmisc_disable_entry(xbe.xbe_name);
5116d756449SSean Bruno 		break;
5126d756449SSean Bruno 
5136d756449SSean Bruno 	case IBC_ENABLE:
5146d756449SSean Bruno 		/* Enable an entry. */
5156d756449SSean Bruno 		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
5166d756449SSean Bruno 		if (error)
5176d756449SSean Bruno 			return (error);
5186d756449SSean Bruno 		if (IBE_VERSION != xbe.xbe_version)
5196d756449SSean Bruno 			return (EINVAL);
5206d756449SSean Bruno 		error = imgact_binmisc_enable_entry(xbe.xbe_name);
5216d756449SSean Bruno 		break;
5226d756449SSean Bruno 
5236d756449SSean Bruno 	case IBC_LOOKUP:
5246d756449SSean Bruno 		/* Lookup an entry. */
5256d756449SSean Bruno 		error = SYSCTL_IN(req, &xbe, sizeof(xbe));
5266d756449SSean Bruno 		if (error)
5276d756449SSean Bruno 			return (error);
5286d756449SSean Bruno 		if (IBE_VERSION != xbe.xbe_version)
5296d756449SSean Bruno 			return (EINVAL);
5306d756449SSean Bruno 		error = imgact_binmisc_lookup_entry(xbe.xbe_name, &xbe);
5316d756449SSean Bruno 		if (!error)
5326d756449SSean Bruno 			error = SYSCTL_OUT(req, &xbe, sizeof(xbe));
5336d756449SSean Bruno 		break;
5346d756449SSean Bruno 
5356d756449SSean Bruno 	case IBC_LIST:
5366d756449SSean Bruno 		/* Return a snapshot of the interpretor list. */
5376d756449SSean Bruno 
5386d756449SSean Bruno 		if (!req->oldptr) {
5396d756449SSean Bruno 			/* No pointer then just return the list size. */
5406d756449SSean Bruno 			error = SYSCTL_OUT(req, 0, interp_list_entry_count *
5416d756449SSean Bruno 			    sizeof(ximgact_binmisc_entry_t));
5426d756449SSean Bruno 			return (error);
5436d756449SSean Bruno 		} else
5446d756449SSean Bruno 			if (!req->oldlen)
5456d756449SSean Bruno 				return (EINVAL);
5466d756449SSean Bruno 
5476d756449SSean Bruno 		error = imgact_binmisc_get_all_entries(req);
5486d756449SSean Bruno 		break;
5496d756449SSean Bruno 
5506d756449SSean Bruno 	default:
5516d756449SSean Bruno 		return (EINVAL);
5526d756449SSean Bruno 	}
5536d756449SSean Bruno 
5546d756449SSean Bruno 	return (error);
5556d756449SSean Bruno }
5566d756449SSean Bruno 
5577029da5cSPawel Biernacki SYSCTL_NODE(_kern, OID_AUTO, binmisc, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
5586d756449SSean Bruno     "Image activator for miscellaneous binaries");
5596d756449SSean Bruno 
5606d756449SSean Bruno SYSCTL_PROC(_kern_binmisc, OID_AUTO, add,
5616d756449SSean Bruno     CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ADD,
5626d756449SSean Bruno     sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
5636d756449SSean Bruno     "Add an activator entry");
5646d756449SSean Bruno 
5656d756449SSean Bruno SYSCTL_PROC(_kern_binmisc, OID_AUTO, remove,
5666d756449SSean Bruno     CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_REMOVE,
5676d756449SSean Bruno     sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
5686d756449SSean Bruno     "Remove an activator entry");
5696d756449SSean Bruno 
5706d756449SSean Bruno SYSCTL_PROC(_kern_binmisc, OID_AUTO, disable,
5716d756449SSean Bruno     CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_DISABLE,
5726d756449SSean Bruno     sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
5736d756449SSean Bruno     "Disable an activator entry");
5746d756449SSean Bruno 
5756d756449SSean Bruno SYSCTL_PROC(_kern_binmisc, OID_AUTO, enable,
5766d756449SSean Bruno     CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_WR, NULL, IBC_ENABLE,
5776d756449SSean Bruno     sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
5786d756449SSean Bruno     "Enable an activator entry");
5796d756449SSean Bruno 
5806d756449SSean Bruno SYSCTL_PROC(_kern_binmisc, OID_AUTO, lookup,
5816d756449SSean Bruno     CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RW|CTLFLAG_ANYBODY, NULL, IBC_LOOKUP,
5826d756449SSean Bruno     sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
5836d756449SSean Bruno     "Lookup an activator entry");
5846d756449SSean Bruno 
5856d756449SSean Bruno SYSCTL_PROC(_kern_binmisc, OID_AUTO, list,
5866d756449SSean Bruno     CTLFLAG_MPSAFE|CTLTYPE_STRUCT|CTLFLAG_RD|CTLFLAG_ANYBODY, NULL, IBC_LIST,
5876d756449SSean Bruno     sysctl_kern_binmisc, "S,ximgact_binmisc_entry",
5886d756449SSean Bruno     "Get snapshot of all the activator entries");
5896d756449SSean Bruno 
5906d756449SSean Bruno static imgact_binmisc_entry_t *
5916d756449SSean Bruno imgact_binmisc_find_interpreter(const char *image_header)
5926d756449SSean Bruno {
5936d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
5946d756449SSean Bruno 	const char *p;
5956d756449SSean Bruno 	int i;
5966d756449SSean Bruno 	size_t sz;
5976d756449SSean Bruno 
5982192cd12SKyle Evans 	INTERP_LIST_ASSERT_LOCKED();
5996d756449SSean Bruno 
6006d756449SSean Bruno 	SLIST_FOREACH(ibe, &interpreter_list, link) {
6016d756449SSean Bruno 		if (!(IBF_ENABLED & ibe->ibe_flags))
6026d756449SSean Bruno 			continue;
6036d756449SSean Bruno 
6046d756449SSean Bruno 		p = image_header + ibe->ibe_moffset;
6056d756449SSean Bruno 		sz = ibe->ibe_msize;
6066d756449SSean Bruno 		if (IBF_USE_MASK & ibe->ibe_flags) {
6076d756449SSean Bruno 			/* Compare using mask. */
6086d756449SSean Bruno 			for (i = 0; i < sz; i++)
6096d756449SSean Bruno 				if ((*p++ ^ ibe->ibe_magic[i]) &
6106d756449SSean Bruno 				    ibe->ibe_mask[i])
6116d756449SSean Bruno 					break;
6126d756449SSean Bruno 		} else {
6136d756449SSean Bruno 			for (i = 0; i < sz; i++)
6146d756449SSean Bruno 				if (*p++ ^ ibe->ibe_magic[i])
6156d756449SSean Bruno 					break;
6166d756449SSean Bruno 		}
6176d756449SSean Bruno 		if (i == ibe->ibe_msize)
6186d756449SSean Bruno 			return (ibe);
6196d756449SSean Bruno 	}
6206d756449SSean Bruno 	return (NULL);
6216d756449SSean Bruno }
6226d756449SSean Bruno 
623945afa7cSSean Bruno static int
6246d756449SSean Bruno imgact_binmisc_exec(struct image_params *imgp)
6256d756449SSean Bruno {
6266d756449SSean Bruno 	const char *image_header = imgp->image_header;
6276d756449SSean Bruno 	const char *fname = NULL;
6286d756449SSean Bruno 	int error = 0;
6291024ef27SKyle Evans #ifdef INVARIANTS
6301024ef27SKyle Evans 	int argv0_cnt = 0;
6311024ef27SKyle Evans #endif
6321024ef27SKyle Evans 	size_t namelen, offset;
6336d756449SSean Bruno 	imgact_binmisc_entry_t *ibe;
6346d756449SSean Bruno 	struct sbuf *sname;
6356d756449SSean Bruno 	char *s, *d;
6366d756449SSean Bruno 
63780083216SKyle Evans 	sname = NULL;
6381024ef27SKyle Evans 	namelen = 0;
6396d756449SSean Bruno 	/* Do we have an interpreter for the given image header? */
6402192cd12SKyle Evans 	INTERP_LIST_RLOCK();
6416d756449SSean Bruno 	if ((ibe = imgact_binmisc_find_interpreter(image_header)) == NULL) {
64280083216SKyle Evans 		error = -1;
64380083216SKyle Evans 		goto done;
6446d756449SSean Bruno 	}
6456d756449SSean Bruno 
6466d756449SSean Bruno 	/* No interpreter nesting allowed. */
64765f20a89SSean Bruno 	if (imgp->interpreted & IMGACT_BINMISC) {
64880083216SKyle Evans 		error = ENOEXEC;
64980083216SKyle Evans 		goto done;
6506d756449SSean Bruno 	}
6516d756449SSean Bruno 
65265f20a89SSean Bruno 	imgp->interpreted |= IMGACT_BINMISC;
6536d756449SSean Bruno 
6541024ef27SKyle Evans 	/*
6551024ef27SKyle Evans 	 * Don't bother with the overhead of putting fname together if we're not
6561024ef27SKyle Evans 	 * using #a.
6571024ef27SKyle Evans 	 */
6581024ef27SKyle Evans 	if (ibe->ibe_argv0_cnt != 0) {
6596d756449SSean Bruno 		if (imgp->args->fname != NULL) {
6606d756449SSean Bruno 			fname = imgp->args->fname;
6616d756449SSean Bruno 		} else {
6626d756449SSean Bruno 			/* Use the fdescfs(5) path for fexecve(2). */
6636d756449SSean Bruno 			sname = sbuf_new_auto();
6646d756449SSean Bruno 			sbuf_printf(sname, "/dev/fd/%d", imgp->args->fd);
6656d756449SSean Bruno 			sbuf_finish(sname);
6666d756449SSean Bruno 			fname = sbuf_data(sname);
6676d756449SSean Bruno 		}
6686d756449SSean Bruno 
6691024ef27SKyle Evans 		namelen = strlen(fname);
6701024ef27SKyle Evans 	}
6711024ef27SKyle Evans 
6726d756449SSean Bruno 	/*
6736d756449SSean Bruno 	 * We need to "push" the interpreter in the arg[] list.  To do this,
6746d756449SSean Bruno 	 * we first shift all the other values in the `begin_argv' area to
6756d756449SSean Bruno 	 * provide the exact amount of room for the values added.  Set up
6766d756449SSean Bruno 	 * `offset' as the number of bytes to be added to the `begin_argv'
6771024ef27SKyle Evans 	 * area.  ibe_interp_offset is the fixed offset from macros present in
6781024ef27SKyle Evans 	 * the interpreter string.
6796d756449SSean Bruno 	 */
6801024ef27SKyle Evans 	offset = ibe->ibe_interp_length + ibe->ibe_interp_offset;
6816d756449SSean Bruno 
6821024ef27SKyle Evans 	/* Variable offset to be added from macros to the interpreter string. */
6831024ef27SKyle Evans 	MPASS(ibe->ibe_argv0_cnt == 0 || namelen > 0);
6841024ef27SKyle Evans 	offset += ibe->ibe_argv0_cnt * (namelen - 2);
6856d756449SSean Bruno 
686f373437aSBrooks Davis 	/* Make room for the interpreter */
687f373437aSBrooks Davis 	error = exec_args_adjust_args(imgp->args, 0, offset);
688f373437aSBrooks Davis 	if (error != 0) {
6896d756449SSean Bruno 		goto done;
6906d756449SSean Bruno 	}
6916d756449SSean Bruno 
6926d756449SSean Bruno 	/* Add the new argument(s) in the count. */
6936d756449SSean Bruno 	imgp->args->argc += ibe->ibe_interp_argcnt;
6946d756449SSean Bruno 
6956d756449SSean Bruno 	/*
6966d756449SSean Bruno 	 * The original arg[] list has been shifted appropriately.  Copy in
6976d756449SSean Bruno 	 * the interpreter path.
6986d756449SSean Bruno 	 */
6996d756449SSean Bruno 	s = ibe->ibe_interpreter;
7006d756449SSean Bruno 	d = imgp->args->begin_argv;
7016d756449SSean Bruno 	while(*s != '\0') {
7026d756449SSean Bruno 		switch (*s) {
7036d756449SSean Bruno 		case '#':
7046d756449SSean Bruno 			/* Handle "#" in interpreter string. */
7056d756449SSean Bruno 			s++;
7066d756449SSean Bruno 			switch(*s) {
7076d756449SSean Bruno 			case ISM_POUND:
7086d756449SSean Bruno 				/* "##": Replace with a single '#' */
7096d756449SSean Bruno 				*d++ = '#';
7106d756449SSean Bruno 				break;
7116d756449SSean Bruno 			case ISM_OLD_ARGV0:
7126d756449SSean Bruno 				/* "#a": Replace with old arg0 (fname). */
7131024ef27SKyle Evans 				MPASS(ibe->ibe_argv0_cnt >= ++argv0_cnt);
7141024ef27SKyle Evans 				memcpy(d, fname, namelen);
7151024ef27SKyle Evans 				d += namelen;
7166d756449SSean Bruno 				break;
7176d756449SSean Bruno 			default:
7181024ef27SKyle Evans 				__assert_unreachable();
7196d756449SSean Bruno 			}
7206d756449SSean Bruno 			break;
7216d756449SSean Bruno 		case ' ':
722e3043798SPedro F. Giffuni 			/* Replace space with NUL to separate arguments. */
7236d756449SSean Bruno 			*d++ = '\0';
7246d756449SSean Bruno 			break;
7256d756449SSean Bruno 		default:
7266d756449SSean Bruno 			*d++ = *s;
7276d756449SSean Bruno 			break;
7286d756449SSean Bruno 		}
7296d756449SSean Bruno 		s++;
7306d756449SSean Bruno 	}
7316d756449SSean Bruno 	*d = '\0';
7326d756449SSean Bruno 
7331024ef27SKyle Evans 	/* Catch ibe->ibe_argv0_cnt counting more #a than we did. */
7341024ef27SKyle Evans 	MPASS(ibe->ibe_argv0_cnt == argv0_cnt);
7356d756449SSean Bruno 	imgp->interpreter_name = imgp->args->begin_argv;
736*5eeb4f73SDoug Rabson 	if (ibe->ibe_interpreter_vnode) {
737*5eeb4f73SDoug Rabson 		imgp->interpreter_vp = ibe->ibe_interpreter_vnode;
738*5eeb4f73SDoug Rabson 		vref(imgp->interpreter_vp);
739*5eeb4f73SDoug Rabson 	}
7406d756449SSean Bruno 
7416d756449SSean Bruno done:
7422192cd12SKyle Evans 	INTERP_LIST_RUNLOCK();
7436d756449SSean Bruno 	if (sname)
7446d756449SSean Bruno 		sbuf_delete(sname);
7456d756449SSean Bruno 	return (error);
7466d756449SSean Bruno }
7476d756449SSean Bruno 
7486d756449SSean Bruno static void
7496d756449SSean Bruno imgact_binmisc_init(void *arg)
7506d756449SSean Bruno {
7516d756449SSean Bruno 
7522192cd12SKyle Evans 	INTERP_LIST_LOCK_INIT();
7536d756449SSean Bruno }
7546d756449SSean Bruno 
7556d756449SSean Bruno static void
7566d756449SSean Bruno imgact_binmisc_fini(void *arg)
7576d756449SSean Bruno {
7586d756449SSean Bruno 	imgact_binmisc_entry_t *ibe, *ibe_tmp;
7596d756449SSean Bruno 
7606d756449SSean Bruno 	/* Free all the interpreters. */
7612192cd12SKyle Evans 	INTERP_LIST_WLOCK();
7626d756449SSean Bruno 	SLIST_FOREACH_SAFE(ibe, &interpreter_list, link, ibe_tmp) {
7636d756449SSean Bruno 		SLIST_REMOVE(&interpreter_list, ibe, imgact_binmisc_entry,
7646d756449SSean Bruno 		    link);
7656d756449SSean Bruno 		imgact_binmisc_destroy_entry(ibe);
7666d756449SSean Bruno 	}
7672192cd12SKyle Evans 	INTERP_LIST_WUNLOCK();
7686d756449SSean Bruno 
7692192cd12SKyle Evans 	INTERP_LIST_LOCK_DESTROY();
7706d756449SSean Bruno }
7716d756449SSean Bruno 
772891cf3edSEd Maste SYSINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_init,
773891cf3edSEd Maste     NULL);
774891cf3edSEd Maste SYSUNINIT(imgact_binmisc, SI_SUB_EXEC, SI_ORDER_MIDDLE, imgact_binmisc_fini,
775891cf3edSEd Maste     NULL);
7766d756449SSean Bruno 
7776d756449SSean Bruno /*
7786d756449SSean Bruno  * Tell kern_execve.c about it, with a little help from the linker.
7796d756449SSean Bruno  */
780b7feabf9SEd Maste static struct execsw imgact_binmisc_execsw = {
781b7feabf9SEd Maste 	.ex_imgact = imgact_binmisc_exec,
782b7feabf9SEd Maste 	.ex_name = KMOD_NAME
783b7feabf9SEd Maste };
7846d756449SSean Bruno EXEC_SET(imgact_binmisc, imgact_binmisc_execsw);
785