diff options
author | dillon <dillon@FreeBSD.org> | 2000-04-26 20:58:40 +0000 |
---|---|---|
committer | dillon <dillon@FreeBSD.org> | 2000-04-26 20:58:40 +0000 |
commit | ce08285d5ac062de6a96e6491313b81bea80ecaa (patch) | |
tree | 9c48fa81d6c9503f7a1fd3b5440dc0d58696221e /sys | |
parent | c816580dfb530d84c2396d9f0d10a9f8e44d4ea4 (diff) | |
download | FreeBSD-src-ce08285d5ac062de6a96e6491313b81bea80ecaa.zip FreeBSD-src-ce08285d5ac062de6a96e6491313b81bea80ecaa.tar.gz |
Fix #! script exec under linux emulation. If a script is exec'd from a
program running under linux emulation, the script binary is checked for
in /compat/linux first. Without this patch the wrong script binary
(i.e. the FreeBSD binary) will be run instead of the linux binary.
For example, #!/bin/sh, thus breaking out of linux compatibility mode.
This solves a number of problems people have had installing linux
software on FreeBSD boxes.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/alpha/linux/linux.h | 2 | ||||
-rw-r--r-- | sys/alpha/linux/linux_sysvec.c | 57 | ||||
-rw-r--r-- | sys/i386/linux/imgact_linux.c | 2 | ||||
-rw-r--r-- | sys/i386/linux/linux.h | 2 | ||||
-rw-r--r-- | sys/i386/linux/linux_sysvec.c | 57 | ||||
-rw-r--r-- | sys/kern/imgact_shell.c | 6 | ||||
-rw-r--r-- | sys/kern/kern_exec.c | 63 | ||||
-rw-r--r-- | sys/sys/imgact.h | 5 | ||||
-rw-r--r-- | sys/sys/sysent.h | 1 |
9 files changed, 156 insertions, 39 deletions
diff --git a/sys/alpha/linux/linux.h b/sys/alpha/linux/linux.h index 528a591..3bb4654 100644 --- a/sys/alpha/linux/linux.h +++ b/sys/alpha/linux/linux.h @@ -224,6 +224,8 @@ struct linux_sigframe { extern int bsd_to_linux_signal[]; extern int linux_to_bsd_signal[]; +extern struct sysentvec linux_sysvec; +extern struct sysentvec elf_linux_sysvec; /* * Pluggable ioctl handlers diff --git a/sys/alpha/linux/linux_sysvec.c b/sys/alpha/linux/linux_sysvec.c index bd2211b..0adf0ab 100644 --- a/sys/alpha/linux/linux_sysvec.c +++ b/sys/alpha/linux/linux_sysvec.c @@ -55,9 +55,16 @@ #include <i386/linux/linux.h> #include <i386/linux/linux_proto.h> +#include <i386/linux/linux_util.h> MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures"); +#if BYTE_ORDER == LITTLE_ENDIAN +#define SHELLMAGIC 0x2123 /* #! */ +#else +#define SHELLMAGIC 0x2321 +#endif + extern char linux_sigcode[]; extern int linux_szsigcode; @@ -401,6 +408,50 @@ linux_prepsyscall(struct trapframe *tf, int *args, u_int *code, caddr_t *params) *params = NULL; /* no copyin */ } +/* + * If a linux binary is exec'ing something, try this image activator + * first. We override standard shell script execution in order to + * be able to modify the interpreter path. We only do this if a linux + * binary is doing the exec, so we do not create an EXEC module for it. + */ +static int exec_linux_imgact_try __P((struct image_params *iparams)); + +static int +exec_linux_imgact_try(imgp) + struct image_params *imgp; +{ + const char *head = (const char *)imgp->image_header; + int error = -1; + + /* + * The interpreter for shell scripts run from a linux binary needs + * to be located in /compat/linux if possible in order to recursively + * maintain linux path emulation. + */ + if (((const short *)head)[0] == SHELLMAGIC) { + /* + * Run our normal shell image activator. If it succeeds attempt + * to use the alternate path for the interpreter. If an alternate + * path is found, use our stringspace to store it. + */ + if ((error = exec_shell_imgact(imgp)) == 0) { + char *rpath = NULL; + + linux_emul_find(imgp->proc, NULL, linux_emul_path, + imgp->interpreter_name, &rpath, 0); + if (rpath != imgp->interpreter_name) { + int len = strlen(rpath) + 1; + + if (len <= MAXSHELLCMDLEN) { + memcpy(imgp->interpreter_name, rpath, len); + } + free(rpath, M_TEMP); + } + } + } + return(error); +} + struct sysentvec linux_sysvec = { LINUX_SYS_MAXSYSCALL, linux_sysent, @@ -416,7 +467,8 @@ struct sysentvec linux_sysvec = { &linux_szsigcode, linux_prepsyscall, "Linux a.out", - aout_coredump + aout_coredump, + exec_linux_imgact_try }; struct sysentvec elf_linux_sysvec = { @@ -434,7 +486,8 @@ struct sysentvec elf_linux_sysvec = { &linux_szsigcode, linux_prepsyscall, "Linux ELF", - elf_coredump + elf_coredump, + exec_linux_imgact_try }; static Elf32_Brandinfo linux_brand = { diff --git a/sys/i386/linux/imgact_linux.c b/sys/i386/linux/imgact_linux.c index 36f6946..c560d74 100644 --- a/sys/i386/linux/imgact_linux.c +++ b/sys/i386/linux/imgact_linux.c @@ -52,8 +52,6 @@ #include <i386/linux/linux.h> -extern struct sysentvec linux_sysvec; - static int exec_linux_imgact __P((struct image_params *iparams)); static int diff --git a/sys/i386/linux/linux.h b/sys/i386/linux/linux.h index 528a591..3bb4654 100644 --- a/sys/i386/linux/linux.h +++ b/sys/i386/linux/linux.h @@ -224,6 +224,8 @@ struct linux_sigframe { extern int bsd_to_linux_signal[]; extern int linux_to_bsd_signal[]; +extern struct sysentvec linux_sysvec; +extern struct sysentvec elf_linux_sysvec; /* * Pluggable ioctl handlers diff --git a/sys/i386/linux/linux_sysvec.c b/sys/i386/linux/linux_sysvec.c index bd2211b..0adf0ab 100644 --- a/sys/i386/linux/linux_sysvec.c +++ b/sys/i386/linux/linux_sysvec.c @@ -55,9 +55,16 @@ #include <i386/linux/linux.h> #include <i386/linux/linux_proto.h> +#include <i386/linux/linux_util.h> MALLOC_DEFINE(M_LINUX, "linux", "Linux mode structures"); +#if BYTE_ORDER == LITTLE_ENDIAN +#define SHELLMAGIC 0x2123 /* #! */ +#else +#define SHELLMAGIC 0x2321 +#endif + extern char linux_sigcode[]; extern int linux_szsigcode; @@ -401,6 +408,50 @@ linux_prepsyscall(struct trapframe *tf, int *args, u_int *code, caddr_t *params) *params = NULL; /* no copyin */ } +/* + * If a linux binary is exec'ing something, try this image activator + * first. We override standard shell script execution in order to + * be able to modify the interpreter path. We only do this if a linux + * binary is doing the exec, so we do not create an EXEC module for it. + */ +static int exec_linux_imgact_try __P((struct image_params *iparams)); + +static int +exec_linux_imgact_try(imgp) + struct image_params *imgp; +{ + const char *head = (const char *)imgp->image_header; + int error = -1; + + /* + * The interpreter for shell scripts run from a linux binary needs + * to be located in /compat/linux if possible in order to recursively + * maintain linux path emulation. + */ + if (((const short *)head)[0] == SHELLMAGIC) { + /* + * Run our normal shell image activator. If it succeeds attempt + * to use the alternate path for the interpreter. If an alternate + * path is found, use our stringspace to store it. + */ + if ((error = exec_shell_imgact(imgp)) == 0) { + char *rpath = NULL; + + linux_emul_find(imgp->proc, NULL, linux_emul_path, + imgp->interpreter_name, &rpath, 0); + if (rpath != imgp->interpreter_name) { + int len = strlen(rpath) + 1; + + if (len <= MAXSHELLCMDLEN) { + memcpy(imgp->interpreter_name, rpath, len); + } + free(rpath, M_TEMP); + } + } + } + return(error); +} + struct sysentvec linux_sysvec = { LINUX_SYS_MAXSYSCALL, linux_sysent, @@ -416,7 +467,8 @@ struct sysentvec linux_sysvec = { &linux_szsigcode, linux_prepsyscall, "Linux a.out", - aout_coredump + aout_coredump, + exec_linux_imgact_try }; struct sysentvec elf_linux_sysvec = { @@ -434,7 +486,8 @@ struct sysentvec elf_linux_sysvec = { &linux_szsigcode, linux_prepsyscall, "Linux ELF", - elf_coredump + elf_coredump, + exec_linux_imgact_try }; static Elf32_Brandinfo linux_brand = { diff --git a/sys/kern/imgact_shell.c b/sys/kern/imgact_shell.c index 78479da..a15b56c 100644 --- a/sys/kern/imgact_shell.c +++ b/sys/kern/imgact_shell.c @@ -39,15 +39,11 @@ #define SHELLMAGIC 0x2321 #endif -#define MAXSHELLCMDLEN 64 - -static int exec_shell_imgact __P((struct image_params *imgp)); - /* * Shell interpreter image activator. A interpreter name beginning * at imgp->stringbase is the minimal successful exit requirement. */ -static int +int exec_shell_imgact(imgp) struct image_params *imgp; { diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 6f40dcc..41a20df 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -106,6 +106,7 @@ execve(p, uap) int error, len, i; struct image_params image_params, *imgp; struct vattr attr; + int (*img_first) __P((struct image_params *)); imgp = &image_params; @@ -174,41 +175,49 @@ interpret: goto exec_fail_dealloc; /* - * Loop through list of image activators, calling each one. - * If there is no match, the activator returns -1. If there - * is a match, but there was an error during the activation, - * the error is returned. Otherwise 0 means success. If the - * image is interpreted, loop back up and try activating - * the interpreter. + * If the current process has a special image activator it + * wants to try first, call it. For example, emulating shell + * scripts differently. */ - for (i = 0; execsw[i]; ++i) { - if (execsw[i]->ex_imgact) - error = (*execsw[i]->ex_imgact)(imgp); - else - continue; - if (error == -1) + error = -1; + if ((img_first = imgp->proc->p_sysent->sv_imgact_try) != NULL) + error = img_first(imgp); + + /* + * Loop through the list of image activators, calling each one. + * An activator returns -1 if there is no match, 0 on success, + * and an error otherwise. + */ + for (i = 0; error == -1 && execsw[i]; ++i) { + if (execsw[i]->ex_imgact == NULL || + execsw[i]->ex_imgact == img_first) { continue; - if (error) - goto exec_fail_dealloc; - if (imgp->interpreted) { - exec_unmap_first_page(imgp); - /* free name buffer and old vnode */ - NDFREE(ndp, NDF_ONLY_PNBUF); - vrele(ndp->ni_vp); - /* set new name to that of the interpreter */ - NDINIT(ndp, LOOKUP, LOCKLEAF | FOLLOW | SAVENAME, - UIO_SYSSPACE, imgp->interpreter_name, p); - goto interpret; } - break; + error = (*execsw[i]->ex_imgact)(imgp); } - /* If we made it through all the activators and none matched, exit. */ - if (error == -1) { - error = ENOEXEC; + + if (error) { + if (error == -1) + error = ENOEXEC; goto exec_fail_dealloc; } /* + * Special interpreter operation, cleanup and loop up to try to + * activate the interpreter. + */ + if (imgp->interpreted) { + exec_unmap_first_page(imgp); + /* free name buffer and old vnode */ + NDFREE(ndp, NDF_ONLY_PNBUF); + vrele(ndp->ni_vp); + /* set new name to that of the interpreter */ + NDINIT(ndp, LOOKUP, LOCKLEAF | FOLLOW | SAVENAME, + UIO_SYSSPACE, imgp->interpreter_name, p); + goto interpret; + } + + /* * Copy out strings (args and env) and initialize stack base */ stack_base = exec_copyout_strings(imgp); diff --git a/sys/sys/imgact.h b/sys/sys/imgact.h index 9cb2e35..c13d81f 100644 --- a/sys/sys/imgact.h +++ b/sys/sys/imgact.h @@ -36,6 +36,8 @@ #ifndef _SYS_IMGACT_H_ #define _SYS_IMGACT_H_ +#define MAXSHELLCMDLEN 64 + struct image_params { struct proc *proc; /* our process struct */ struct execve_args *uap; /* syscall arguments */ @@ -51,7 +53,7 @@ struct image_params { unsigned long entry_addr; /* entry address of target executable */ char vmspace_destroyed; /* flag - we've blown away original vm space */ char interpreted; /* flag - this executable is interpreted */ - char interpreter_name[64]; /* name of the interpreter */ + char interpreter_name[MAXSHELLCMDLEN]; /* name of the interpreter */ void *auxargs; /* ELF Auxinfo structure pointer */ struct vm_page *firstpage; /* first page that we mapped */ char *fname; /* pointer to filename of executable (user space) */ @@ -62,6 +64,7 @@ struct image_params { int exec_check_permissions __P((struct image_params *)); int exec_extract_strings __P((struct image_params *)); int exec_new_vmspace __P((struct image_params *)); +int exec_shell_imgact __P((struct image_params *)); #endif #endif /* !_SYS_IMGACT_H_ */ diff --git a/sys/sys/sysent.h b/sys/sys/sysent.h index fff1ed0..15dbdbf 100644 --- a/sys/sys/sysent.h +++ b/sys/sys/sysent.h @@ -79,6 +79,7 @@ struct sysentvec { int (*sv_coredump) __P((struct proc *, struct vnode *, off_t)); /* function to dump core, or NULL */ + int (*sv_imgact_try) __P((struct image_params *)); }; #ifdef _KERNEL |