diff --git a/sys/kern/imgact_binmisc.c b/sys/kern/imgact_binmisc.c --- a/sys/kern/imgact_binmisc.c +++ b/sys/kern/imgact_binmisc.c @@ -30,15 +30,18 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include +#include #include @@ -63,6 +66,7 @@ uint8_t *ibe_magic; uint8_t *ibe_mask; uint8_t *ibe_interpreter; + struct vnode *ibe_interpreter_vnode; ssize_t ibe_interp_offset; uint32_t ibe_interp_argcnt; uint32_t ibe_interp_length; @@ -114,7 +118,7 @@ * Populate the entry with the information about the interpreter. */ static void -imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe) +imgact_binmisc_populate_interp(char *str, imgact_binmisc_entry_t *ibe, int flags) { uint32_t len = 0, argc = 1; char t[IBE_INTERP_LEN_MAX]; @@ -150,6 +154,30 @@ memcpy(ibe->ibe_interpreter, t, len); ibe->ibe_interp_argcnt = argc; ibe->ibe_interp_length = len; + + ibe->ibe_interpreter_vnode = NULL; + if (flags & IBF_PRE_OPEN) { + struct nameidata nd; + int error; + + tp = t; + while (*tp != '\0' && *tp != ' ') { + tp++; + } + *tp = '\0'; + NDINIT(&nd, LOOKUP, FOLLOW | ISOPEN, UIO_SYSSPACE, t); + + /* + * If there is an error, just stop now and fall back + * to the non pre-open case where we lookup during + * exec. + */ + error = namei(&nd); + if (error) + return; + + ibe->ibe_interpreter_vnode = nd.ni_vp; + } } /* @@ -167,7 +195,7 @@ ibe->ibe_name = malloc(namesz, M_BINMISC, M_WAITOK|M_ZERO); strlcpy(ibe->ibe_name, xbe->xbe_name, namesz); - imgact_binmisc_populate_interp(xbe->xbe_interpreter, ibe); + imgact_binmisc_populate_interp(xbe->xbe_interpreter, ibe, xbe->xbe_flags); ibe->ibe_magic = malloc(xbe->xbe_msize, M_BINMISC, M_WAITOK|M_ZERO); memcpy(ibe->ibe_magic, xbe->xbe_magic, xbe->xbe_msize); @@ -199,6 +227,8 @@ free(ibe->ibe_interpreter, M_BINMISC); if (ibe->ibe_name) free(ibe->ibe_name, M_BINMISC); + if (ibe->ibe_interpreter_vnode) + vrele(ibe->ibe_interpreter_vnode); if (ibe) free(ibe, M_BINMISC); } @@ -271,15 +301,20 @@ } } + /* + * Preallocate a new entry. We do this without holding the + * lock to avoid lock-order problems if IBF_PRE_OPEN is + * set. + */ + ibe = imgact_binmisc_new_entry(xbe, interp_offset, argv0_cnt); + INTERP_LIST_WLOCK(); if (imgact_binmisc_find_entry(xbe->xbe_name) != NULL) { INTERP_LIST_WUNLOCK(); + imgact_binmisc_destroy_entry(ibe); return (EEXIST); } - /* Preallocate a new entry. */ - ibe = imgact_binmisc_new_entry(xbe, interp_offset, argv0_cnt); - SLIST_INSERT_HEAD(&interpreter_list, ibe, link); interp_list_entry_count++; INTERP_LIST_WUNLOCK(); @@ -698,6 +733,10 @@ /* Catch ibe->ibe_argv0_cnt counting more #a than we did. */ MPASS(ibe->ibe_argv0_cnt == argv0_cnt); imgp->interpreter_name = imgp->args->begin_argv; + if (ibe->ibe_interpreter_vnode) { + imgp->interpreter_vp = ibe->ibe_interpreter_vnode; + vref(imgp->interpreter_vp); + } done: INTERP_LIST_RUNLOCK(); diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -504,6 +504,18 @@ imgp->execpath = args->fname; vn_lock(imgp->vp, LK_SHARED | LK_RETRY); } + } else if (imgp->interpreter_vp) { + /* + * An image activator has already provided an open vnode + */ + newtextvp = imgp->interpreter_vp; + imgp->interpreter_vp = NULL; + if (vn_fullpath(newtextvp, &imgp->execpath, + &imgp->freepath) != 0) + imgp->execpath = args->fname; + vn_lock(newtextvp, LK_SHARED | LK_RETRY); + AUDIT_ARG_VNODE1(newtextvp); + imgp->vp = newtextvp; } else { AUDIT_ARG_FD(args->fd); @@ -702,7 +714,11 @@ free(imgp->freepath, M_TEMP); imgp->freepath = NULL; /* set new name to that of the interpreter */ - args->fname = imgp->interpreter_name; + if (imgp->interpreter_vp) { + args->fname = NULL; + } else { + args->fname = imgp->interpreter_name; + } goto interpret; } diff --git a/sys/sys/imgact.h b/sys/sys/imgact.h --- a/sys/sys/imgact.h +++ b/sys/sys/imgact.h @@ -94,6 +94,7 @@ u_int map_flags; #define IMGP_ASLR_SHARED_PAGE 0x1 uint32_t imgp_flags; + struct vnode *interpreter_vp; /* vnode of the interpreter */ }; #ifdef _KERNEL diff --git a/sys/sys/imgact_binmisc.h b/sys/sys/imgact_binmisc.h --- a/sys/sys/imgact_binmisc.h +++ b/sys/sys/imgact_binmisc.h @@ -57,8 +57,9 @@ */ #define IBF_ENABLED 0x0001 /* Entry is active. */ #define IBF_USE_MASK 0x0002 /* Use mask on header magic field. */ +#define IBF_PRE_OPEN 0x0004 /* Cache the vnode for interpreter */ -#define IBF_VALID_UFLAGS 0x0003 /* Bits allowed from userland. */ +#define IBF_VALID_UFLAGS 0x0007 /* Bits allowed from userland. */ /* * Used with sysctlbyname() to pass imgact bin misc entries in and out of the diff --git a/usr.sbin/binmiscctl/binmiscctl.8 b/usr.sbin/binmiscctl/binmiscctl.8 --- a/usr.sbin/binmiscctl/binmiscctl.8 +++ b/usr.sbin/binmiscctl/binmiscctl.8 @@ -46,6 +46,7 @@ .Op Fl -mask Ar mask .Op Fl -offset Ar offset .Op Fl -set-enabled +.Op Fl -pre-open .Nm .Cm disable .Ar name @@ -87,6 +88,7 @@ .Op Fl -mask Ar mask .Op Fl -offset Ar offset .Op Fl -set-enabled +.Op Fl -pre-open .Xc Add a new activator entry in the kernel. You must specify a @@ -124,6 +126,12 @@ option is used. The activator default state is disabled. .Pp +To make the interpreter automatically available in jails and chroots, +use the +.Fl -pre-open +option to allow the kernel to open the binary at configuration time +rather then lazily when the the interpreted program is started. +.Pp The interpreter .Ar path may also contain arguments for the interpreter including diff --git a/usr.sbin/binmiscctl/binmiscctl.c b/usr.sbin/binmiscctl/binmiscctl.c --- a/usr.sbin/binmiscctl/binmiscctl.c +++ b/usr.sbin/binmiscctl/binmiscctl.c @@ -75,7 +75,8 @@ " --interpreter \\\n" "\t\t--magic [--mask ] \\\n" "\t\t--size [--offset ] \\\n" - "\t\t[--set-enabled]" + "\t\t[--set-enabled] \\\n" + "\t\t[--pre-open]" }, { CMD_REMOVE, @@ -122,6 +123,7 @@ { "magic", required_argument, NULL, 'm' }, { "offset", required_argument, NULL, 'o' }, { "size", required_argument, NULL, 's' }, + { "pre-open", no_argument, NULL, 'p' }, { NULL, 0, NULL, 0 } }; @@ -192,8 +194,9 @@ printf("name: %s\n", xbe->xbe_name); printf("interpreter: %s\n", xbe->xbe_interpreter); - printf("flags: %s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "", - (flags & IBF_USE_MASK) ? "USE_MASK " : ""); + printf("flags: %s%s%s\n", (flags & IBF_ENABLED) ? "ENABLED " : "", + (flags & IBF_USE_MASK) ? "USE_MASK " : "", + (flags & IBF_PRE_OPEN) ? "PRE_OPEN " : ""); printf("magic size: %u\n", xbe->xbe_msize); printf("magic offset: %u\n", xbe->xbe_moffset); @@ -291,7 +294,7 @@ IBE_NAME_MAX); strlcpy(&xbe->xbe_name[0], argv[0], IBE_NAME_MAX); - while ((ch = getopt_long(argc, argv, "ei:m:M:o:s:", add_opts, NULL)) + while ((ch = getopt_long(argc, argv, "epi:m:M:o:s:", add_opts, NULL)) != -1) { switch(ch) { @@ -328,6 +331,10 @@ xbe->xbe_msize); break; + case 'p': + xbe->xbe_flags |= IBF_PRE_OPEN; + break; + default: usage("Unknown argument: '%c'", ch); }