summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/man/man2/exec.216
-rw-r--r--usr/src/uts/common/exec/elf/elf.c8
-rw-r--r--usr/src/uts/common/exec/intp/intp.c73
-rw-r--r--usr/src/uts/common/exec/java/java.c6
-rw-r--r--usr/src/uts/common/exec/shbin/shbin.c4
-rw-r--r--usr/src/uts/common/os/brand.c6
-rw-r--r--usr/src/uts/common/os/exec.c25
-rw-r--r--usr/src/uts/common/os/zone.c4
-rw-r--r--usr/src/uts/common/sys/exec.h5
-rw-r--r--usr/src/uts/common/sys/zone.h3
10 files changed, 113 insertions, 37 deletions
diff --git a/usr/src/man/man2/exec.2 b/usr/src/man/man2/exec.2
index ec5cbe64f3..c21503690e 100644
--- a/usr/src/man/man2/exec.2
+++ b/usr/src/man/man2/exec.2
@@ -1,5 +1,6 @@
'\" te
.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright 2015, Joyent, Inc.
.\" Copyright 1989 AT&T.
.\" Portions Copyright (c) 1992, X/Open Company Limited. All Rights Reserved.
.\" Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission to reproduce portions of its copyrighted documentation. Original documentation from The Open Group can be obtained online at
@@ -9,7 +10,7 @@
.\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License.
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. See the License for the specific language governing permissions and limitations under the License.
.\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH EXEC 2 "Jun 16, 2008"
+.TH EXEC 2 "Oct 27, 2015"
.SH NAME
exec, execl, execle, execlp, execv, execve, execvp \- execute a file
.SH SYNOPSIS
@@ -50,7 +51,6 @@ exec, execl, execle, execlp, execv, execve, execvp \- execute a file
.fi
.SH DESCRIPTION
-.sp
.LP
Each of the functions in the \fBexec\fR family replaces the current process
image with a new process image. The new image is constructed from a regular,
@@ -76,7 +76,9 @@ specified interpreter. The pathname specified in the interpreter file is passed
as \fIarg0\fR to the interpreter. If \fIarg\fR was specified in the interpreter
file, it is passed as \fIarg1\fR to the interpreter. The remaining arguments to
the interpreter are \fIarg0\fR through \fIargn\fR of the originally exec'd
-file. The interpreter named by \fIpathname\fR must not be an interpreter file.
+file. The interpreter named by \fIpathname\fR may also be an interpreter file.
+There can be up to four nested interpreter files before the final interpreter.
+The setid bits on nested interpreters are silently ignored.
.sp
.LP
When a C-language program is executed as a result of this call, it is entered
@@ -499,13 +501,11 @@ consequence of replacing the process image.
The saved resource limits in the new process image are set to be a copy of the
process's corresponding hard and soft limits.
.SH RETURN VALUES
-.sp
.LP
If a function in the \fBexec\fR family returns to the calling process image, an
error has occurred; the return value is \fB\(mi1\fR and \fBerrno\fR is set to
indicate the error.
.SH ERRORS
-.sp
.LP
The \fBexec\fR functions will fail if:
.sp
@@ -587,7 +587,7 @@ A signal was caught during the execution of one of the functions in the
.ad
.RS 16n
Too many symbolic links were encountered in translating \fIpath\fR or
-\fIfile\fR.
+\fIfile\fR, or too many nested interpreter files.
.RE
.sp
@@ -678,7 +678,6 @@ currently open for writing by some process.
.RE
.SH USAGE
-.sp
.LP
As the state of conversion descriptors and message catalogue descriptors in the
new process image is undefined, portable applications should not rely on their
@@ -692,7 +691,6 @@ thenew process.
.LP
The \fIenviron\fR array should not be accessed directly by the application.
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -715,7 +713,6 @@ Standard See \fBstandards\fR(5).
.LP
The \fBexecle()\fR and \fBexecve()\fR fucntions are Async-Signal-Safe.
.SH SEE ALSO
-.sp
.LP
\fBksh\fR(1), \fBps\fR(1), \fBsh\fR(1), \fBalarm\fR(2), \fBbrk\fR(2),
\fBchmod\fR(2), \fBexit\fR(2), \fBfcntl\fR(2), \fBfork\fR(2),
@@ -727,7 +724,6 @@ The \fBexecle()\fR and \fBexecve()\fR fucntions are Async-Signal-Safe.
\fBcontract\fR(4), \fBprocess\fR(4), \fBattributes\fR(5), \fBenviron\fR(5),
\fBprivileges\fR(5), \fBstandards\fR(5)
.SH WARNINGS
-.sp
.LP
If a program is \fBsetuid\fR to a user \fBID\fR other than the superuser, and
the program is executed when the real user \fBID\fR is super-user, then the
diff --git a/usr/src/uts/common/exec/elf/elf.c b/usr/src/uts/common/exec/elf/elf.c
index d244c9cc81..5f6bee2378 100644
--- a/usr/src/uts/common/exec/elf/elf.c
+++ b/usr/src/uts/common/exec/elf/elf.c
@@ -344,8 +344,14 @@ elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
* We do this because now the brand library can just check
* args->to_model to see if the target is 32-bit or 64-bit without
* having do duplicate all the code above.
+ *
+ * The level checks associated with brand handling below are used to
+ * prevent a loop since the brand elfexec function typically comes back
+ * through this function. We must check <= here since the nested
+ * handling in the #! interpreter code will increment the level before
+ * calling gexec to run the final elfexec interpreter.
*/
- if ((level < 2) &&
+ if ((level <= INTP_MAXDEPTH) &&
(brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
error = BROP(p)->b_elfexec(vp, uap, args,
idatap, level + 1, execsz, setid, exec_file, cred,
diff --git a/usr/src/uts/common/exec/intp/intp.c b/usr/src/uts/common/exec/intp/intp.c
index 998c0f1f8e..269ba86b1b 100644
--- a/usr/src/uts/common/exec/intp/intp.c
+++ b/usr/src/uts/common/exec/intp/intp.c
@@ -46,6 +46,7 @@
#include <sys/exec.h>
#include <sys/kmem.h>
#include <sys/note.h>
+#include <sys/sdt.h>
/*
* This is the loadable module wrapper.
@@ -137,19 +138,19 @@ getintphead(struct vnode *vp, struct intpdata *idatap)
;
if (*cp == '\0')
return (ENOEXEC);
- idatap->intp_name = cp;
+ idatap->intp_name[0] = cp;
while (*cp && *cp != ' ')
cp++;
- if (*cp == '\0')
- idatap->intp_arg = NULL;
- else {
+ if (*cp == '\0') {
+ idatap->intp_arg[0] = NULL;
+ } else {
*cp++ = '\0';
while (*cp == ' ')
cp++;
if (*cp == '\0')
- idatap->intp_arg = NULL;
+ idatap->intp_arg[0] = NULL;
else {
- idatap->intp_arg = cp;
+ idatap->intp_arg[0] = cp;
while (*cp && *cp != ' ')
cp++;
*cp = '\0';
@@ -158,6 +159,24 @@ getintphead(struct vnode *vp, struct intpdata *idatap)
return (0);
}
+/*
+ * We support nested interpreters up to a depth of INTP_MAXDEPTH (this value
+ * matches the depth on Linux). When a nested interpreter is in use, the
+ * previous name and argument must be passed along. We use the intpdata_t
+ * name and argument arrays for this. In the normal, non-nested case, only the
+ * first element in those arrays will be populated.
+ *
+ * For setid scripts the "script hole" is a security race condition between
+ * when we exec the interpreter and when the interpreter reads the script. We
+ * handle this below for the initial script, but we don't allow setid scripts
+ * when using nested interpreters. Because gexec only modifies the credentials
+ * for a setid script at level 0, then if we come back through for a nested
+ * interpreter we know that args->fname will be set (the first script is setid)
+ * and we can return an error. If an intermediate nested interpreter is setid
+ * then it will not be run with different credentials because of the gexec
+ * handling, so it is effectively no longer setid and we don't have to worry
+ * about the "script hole".
+ */
int
intpexec(
struct vnode *vp,
@@ -181,12 +200,15 @@ intpexec(
char devfd[19]; /* 32-bit int fits in 10 digits + 8 for "/dev/fd/" */
int fd = -1;
- if (level) { /* Can't recurse */
- error = ENOEXEC;
+ if (level >= INTP_MAXDEPTH) { /* Can't recurse past maxdepth */
+ error = ELOOP;
goto bad;
}
- ASSERT(idatap == (struct intpdata *)NULL);
+ if (level == 0)
+ ASSERT(idatap == (struct intpdata *)NULL);
+
+ bzero(&idata, sizeof (intpdata_t));
/*
* Allocate a buffer to read in the interpreter pathname.
@@ -198,7 +220,7 @@ intpexec(
/*
* Look the new vnode up.
*/
- if (error = pn_get(idata.intp_name, UIO_SYSSPACE, &intppn))
+ if (error = pn_get(idata.intp_name[0], UIO_SYSSPACE, &intppn))
goto fail;
pn_alloc(&resolvepn);
if (error = lookuppn(&intppn, &resolvepn, FOLLOW, NULLVPP, &nvp)) {
@@ -206,12 +228,43 @@ intpexec(
pn_free(&intppn);
goto fail;
}
+
+ if (level > 0) {
+ /*
+ * We have a nested interpreter. The previous name(s) and
+ * argument(s) need to be passed along. We also keep track
+ * of how often this zone uses nested interpreters.
+ */
+ int i;
+
+ atomic_inc_32(&curproc->p_zone->zone_nested_intp);
+
+ ASSERT(idatap != NULL);
+ /* since we're shifting up, loop stops one short */
+ for (i = 0; i < (INTP_MAXDEPTH - 1); i++) {
+ idata.intp_name[i + 1] = idatap->intp_name[i];
+ idata.intp_arg[i + 1] = idatap->intp_arg[i];
+ }
+
+ DTRACE_PROBE3(nested__intp, int, level, void *, &idata,
+ void *, nvp);
+ }
+
opath = args->pathname;
args->pathname = resolvepn.pn_path;
/* don't free resolvepn until we are done with args */
pn_free(&intppn);
/*
+ * Disallow setuid or additional privilege execution for nested
+ * interpreters.
+ */
+ if (level > 0 && args->fname != NULL) {
+ error = ENOEXEC;
+ goto done;
+ }
+
+ /*
* When we're executing a set-uid script resulting in uids
* mismatching or when we execute with additional privileges,
* we close the "replace script between exec and open by shell"
diff --git a/usr/src/uts/common/exec/java/java.c b/usr/src/uts/common/exec/java/java.c
index 7cf9126e8d..fdc327dcbb 100644
--- a/usr/src/uts/common/exec/java/java.c
+++ b/usr/src/uts/common/exec/java/java.c
@@ -145,9 +145,9 @@ javaexec(vnode_t *vp, struct execa *uap, struct uarg *args,
* Find and invoke the Java runtime environment on the file
*/
idata.intp = NULL;
- idata.intp_name = jexec;
- idata.intp_arg = jexec_arg;
- if (error = pn_get(idata.intp_name, UIO_SYSSPACE, &lookpn))
+ idata.intp_name[0] = jexec;
+ idata.intp_arg[0] = jexec_arg;
+ if (error = pn_get(idata.intp_name[0], UIO_SYSSPACE, &lookpn))
return (error);
pn_alloc(&resolvepn);
if (error = lookuppn(&lookpn, &resolvepn, FOLLOW, NULLVPP, &nvp)) {
diff --git a/usr/src/uts/common/exec/shbin/shbin.c b/usr/src/uts/common/exec/shbin/shbin.c
index a6cf129d9d..ee5060a07e 100644
--- a/usr/src/uts/common/exec/shbin/shbin.c
+++ b/usr/src/uts/common/exec/shbin/shbin.c
@@ -221,8 +221,8 @@ shbinexec(
* a script's name starts with a '-' character.
*/
idata.intp = NULL;
- idata.intp_name = shell_list[i];
- idata.intp_arg = "--";
+ idata.intp_name[0] = shell_list[i];
+ idata.intp_arg[0] = "--";
opath = args->pathname;
args->pathname = resolvepn.pn_path;
diff --git a/usr/src/uts/common/os/brand.c b/usr/src/uts/common/os/brand.c
index eb8c6e730a..0af67f5d98 100644
--- a/usr/src/uts/common/os/brand.c
+++ b/usr/src/uts/common/os/brand.c
@@ -672,13 +672,13 @@ brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
orig_sigaltstack.ss_flags = lwp->lwp_sigaltstack.ss_flags;
if (args->to_model == DATAMODEL_NATIVE) {
- err = elfexec(nvp, uap, args, idatap, level + 1, execsz,
+ err = elfexec(nvp, uap, args, idatap, INTP_MAXDEPTH + 1, execsz,
setid, exec_file, cred, brand_action);
}
#if defined(_LP64)
else {
- err = elf32exec(nvp, uap, args, idatap, level + 1, execsz,
- setid, exec_file, cred, brand_action);
+ err = elf32exec(nvp, uap, args, idatap, INTP_MAXDEPTH + 1,
+ execsz, setid, exec_file, cred, brand_action);
}
#endif /* _LP64 */
VN_RELE(nvp);
diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c
index f7c565e546..172fce8d89 100644
--- a/usr/src/uts/common/os/exec.c
+++ b/usr/src/uts/common/os/exec.c
@@ -1556,13 +1556,26 @@ stk_copyin(execa_t *uap, uarg_t *args, intpdata_t *intp, void **auxvpp)
/*
* Copy interpreter's name and argument to argv[0] and argv[1].
+ * In the rare case that we have nested interpreters then those names
+ * and arguments are also copied to the subsequent slots in argv.
*/
- if (intp != NULL && intp->intp_name != NULL) {
- if ((error = stk_add(args, intp->intp_name, UIO_SYSSPACE)) != 0)
- return (error);
- if (intp->intp_arg != NULL &&
- (error = stk_add(args, intp->intp_arg, UIO_SYSSPACE)) != 0)
- return (error);
+ if (intp != NULL && intp->intp_name[0] != NULL) {
+ int i;
+
+ for (i = 0; i < INTP_MAXDEPTH; i++) {
+ if (intp->intp_name[i] == NULL)
+ break;
+ error = stk_add(args, intp->intp_name[i], UIO_SYSSPACE);
+ if (error != 0)
+ return (error);
+ if (intp->intp_arg[i] != NULL) {
+ error = stk_add(args, intp->intp_arg[i],
+ UIO_SYSSPACE);
+ if (error != 0)
+ return (error);
+ }
+ }
+
if (args->fname != NULL)
error = stk_add(args, args->fname, UIO_SYSSPACE);
else
diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c
index 2f041fdb03..c997f8fd8d 100644
--- a/usr/src/uts/common/os/zone.c
+++ b/usr/src/uts/common/os/zone.c
@@ -1902,6 +1902,8 @@ zone_misc_kstat_update(kstat_t *ksp, int rw)
zmp->zm_ffnomem.value.ui32 = zone->zone_ffnomem;
zmp->zm_ffmisc.value.ui32 = zone->zone_ffmisc;
+ zmp->zm_nested_intp.value.ui32 = zone->zone_nested_intp;
+
zmp->zm_init_pid.value.ui32 = zone->zone_proc_initpid;
zmp->zm_boot_time.value.ui64 = (uint64_t)zone->zone_boot_time;
@@ -1943,6 +1945,8 @@ zone_misc_kstat_create(zone_t *zone)
KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_ffnomem, "forkfail_nomem", KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_ffmisc, "forkfail_misc", KSTAT_DATA_UINT32);
+ kstat_named_init(&zmp->zm_nested_intp, "nested_interp",
+ KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_init_pid, "init_pid", KSTAT_DATA_UINT32);
kstat_named_init(&zmp->zm_boot_time, "boot_time", KSTAT_DATA_UINT64);
diff --git a/usr/src/uts/common/sys/exec.h b/usr/src/uts/common/sys/exec.h
index d36bc20481..b5e2c58be5 100644
--- a/usr/src/uts/common/sys/exec.h
+++ b/usr/src/uts/common/sys/exec.h
@@ -157,10 +157,11 @@ typedef struct uarg {
#endif
#define INTPSZ MAXPATHLEN
+#define INTP_MAXDEPTH 5 /* Nested interpreter depth matches Linux */
typedef struct intpdata {
char *intp;
- char *intp_name;
- char *intp_arg;
+ char *intp_name[INTP_MAXDEPTH];
+ char *intp_arg[INTP_MAXDEPTH];
} intpdata_t;
#define EXECSETID_SETID 0x1 /* setid exec */
diff --git a/usr/src/uts/common/sys/zone.h b/usr/src/uts/common/sys/zone.h
index 34d00c3e89..2e69b0d1c7 100644
--- a/usr/src/uts/common/sys/zone.h
+++ b/usr/src/uts/common/sys/zone.h
@@ -403,6 +403,7 @@ typedef struct {
kstat_named_t zm_ffnoproc;
kstat_named_t zm_ffnomem;
kstat_named_t zm_ffmisc;
+ kstat_named_t zm_nested_intp;
kstat_named_t zm_init_pid;
kstat_named_t zm_boot_time;
} zone_misc_kstat_t;
@@ -601,6 +602,8 @@ typedef struct zone {
uint32_t zone_ffnomem; /* as_dup/memory error */
uint32_t zone_ffmisc; /* misc. other error */
+ uint32_t zone_nested_intp; /* nested interp. kstat */
+
struct loadavg_s zone_loadavg; /* loadavg for this zone */
uint64_t zone_hp_avenrun[3]; /* high-precision avenrun */
int zone_avenrun[3]; /* FSCALED avg. run queue len */