From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 6453 invoked by alias); 17 Apr 2015 13:26:18 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 34909 Received: (qmail 295 invoked from network); 17 Apr 2015 13:26:04 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_HI, SPF_HELO_PASS autolearn=ham version=3.3.2 From: Kamil Dudka To: zsh-workers@zsh.org Subject: [PATCH 1/2] report bad ELF interpreter if it causes exec to fail Date: Fri, 17 Apr 2015 15:25:54 +0200 Message-Id: <1429277155-24607-1-git-send-email-kdudka@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 This is already implemented in some distributions of bash and tcsh . Steps to reproduce: echo 'int main () { return 0; }' > u.c gcc -o u u.c -Wl,-dynamic-linker,/foo/bar/baz zsh -c ./u Bug: https://bugzilla.redhat.com/711067 --- Src/exec.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- configure.ac | 2 +- 2 files changed, 131 insertions(+), 4 deletions(-) diff --git a/Src/exec.c b/Src/exec.c index 2a8185c..18408d7 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -30,6 +30,10 @@ #include "zsh.mdh" #include "exec.pro" +#ifdef HAVE_ELF_H +# include +#endif + /* Flags for last argument of addvars */ enum { @@ -427,6 +431,112 @@ execcursh(Estate state, int do_exec) return lastval; } +/* The following code is taken from . */ +#ifdef HAVE_ELF_H +static int +checkelfinterp(const char *pth, int fd, const char *sample, int sample_len) +{ + off_t offset = -1; + int eno = ENOENT; + + /* Read the offset of the interpreter string. */ + if (sample[EI_CLASS] == ELFCLASS32 && sample_len >= sizeof(Elf32_Ehdr)) { + Elf32_Ehdr ehdr; + Elf32_Phdr *phdr; + Elf32_Half nphdr; + + /* + * We have to copy the data since the sample buffer might not be + * aligned correctly to be accessed as an Elf32_Ehdr struct. + */ + memcpy(&ehdr, sample, sizeof(Elf32_Ehdr)); + + nphdr = ehdr.e_phnum; + phdr = (Elf32_Phdr *) zalloc((size_t)nphdr * (size_t)ehdr.e_phentsize); + if (phdr != NULL) { + if (lseek(fd, ehdr.e_phoff, SEEK_SET) != -1) + sample_len = read(fd, phdr, nphdr * ehdr.e_phentsize); + else + sample_len = -1; + + if (sample_len == nphdr * ehdr.e_phentsize) + while (nphdr-- > 0) + if (phdr[nphdr].p_type == PT_INTERP) { + offset = phdr[nphdr].p_offset; + break; + } + free(phdr); + } + } else if (sample[EI_CLASS] == ELFCLASS64 + && sample_len >= sizeof(Elf64_Ehdr)) { + Elf64_Ehdr ehdr; + Elf64_Phdr *phdr; + Elf32_Half nphdr; + + /* + * We have to copy the data since the sample buffer might not be + * aligned correctly to be accessed as an Elf64_Ehdr struct. + */ + memcpy(&ehdr, sample, sizeof(Elf64_Ehdr)); + + nphdr = ehdr.e_phnum; + phdr = (Elf64_Phdr *) zalloc((size_t)nphdr * (size_t)ehdr.e_phentsize); + if (phdr != NULL) { + if (lseek(fd, ehdr.e_phoff, SEEK_SET) != -1) + sample_len = read(fd, phdr, + nphdr * ehdr.e_phentsize); + else + sample_len = -1; + + if (sample_len == nphdr * ehdr.e_phentsize) + while (nphdr-- > 0) + if (phdr[nphdr].p_type == PT_INTERP) { + offset = phdr[nphdr].p_offset; + break; + } + free(phdr); + } + } + + if (offset != -1) { + ssize_t maxlen = 0; + ssize_t actlen = 0; + ssize_t nread = 0; + off_t pos = 0; + char *interp = NULL; + + for (;;) { + if (actlen >= maxlen) { + char *newinterp = zrealloc(interp, maxlen += 200); + if (newinterp == NULL) + /* out of memroy */ + break; + interp = newinterp; + } + + if (lseek(fd, offset + pos, SEEK_SET) == -1) + break; + + if ((nread = read(fd, interp + pos, maxlen - pos)) == -1) + break; + + if (memchr(interp + pos, '\0', nread) != NULL) { + zerr("%s: %s: bad ELF interpreter", pth, interp); + eno = ENOEXEC; + break; + } + + actlen += nread; + pos += nread; + } + + free(interp); + } + + return eno; +} +#endif + /* execve after handling $_ and #! */ #define POUNDBANGLIMIT 64 @@ -468,12 +578,20 @@ zexecve(char *pth, char **argv, char **newenvp) int fd, ct, t0; if ((fd = open(pth, O_RDONLY|O_NOCTTY)) >= 0) { + size_t hdrsize = POUNDBANGLIMIT; +#ifdef HAVE_ELF_H + /* Inspect 32 and 64 ELF */ + if (sizeof(Elf64_Ehdr) > hdrsize) + hdrsize = sizeof(Elf64_Ehdr); + if (sizeof(Elf32_Ehdr) > hdrsize) + hdrsize = sizeof(Elf32_Ehdr); +#endif argv0 = *argv; *argv = pth; - ct = read(fd, execvebuf, POUNDBANGLIMIT); - close(fd); + ct = read(fd, execvebuf, hdrsize); if (ct > 0) { if (execvebuf[0] == '#') { + close(fd); if (execvebuf[1] == '!') { for (t0 = 0; t0 != ct; t0++) if (execvebuf[t0] == '\n') @@ -513,6 +631,7 @@ zexecve(char *pth, char **argv, char **newenvp) execve("/bin/sh", argv - 1, newenvp); } } else if (eno == ENOEXEC) { + close(fd); for (t0 = 0; t0 != ct; t0++) if (!execvebuf[t0]) break; @@ -521,7 +640,15 @@ zexecve(char *pth, char **argv, char **newenvp) winch_unblock(); execve("/bin/sh", argv - 1, newenvp); } - } +#ifdef HAVE_ELF_H + } else if (eno == ENOENT + && ct > EI_NIDENT + && memcmp(execvebuf, ELFMAG, SELFMAG) == 0) { + eno = checkelfinterp(pth, fd, execvebuf, ct); + close(fd); +#endif + } else + close(fd); } else eno = errno; *argv = argv0; diff --git a/configure.ac b/configure.ac index e4de193..645385d 100644 --- a/configure.ac +++ b/configure.ac @@ -676,7 +676,7 @@ AC_CHECK_HEADERS(sys/time.h sys/times.h sys/select.h termcap.h termio.h \ utmp.h utmpx.h sys/types.h pwd.h grp.h poll.h sys/mman.h \ netinet/in_systm.h pcre.h langinfo.h wchar.h stddef.h \ sys/stropts.h iconv.h ncurses.h ncursesw/ncurses.h \ - ncurses/ncurses.h) + ncurses/ncurses.h elf.h) if test x$dynamic = xyes; then AC_CHECK_HEADERS(dlfcn.h) AC_CHECK_HEADERS(dl.h) -- 2.1.0