zsh-workers
 help / color / mirror / code / Atom feed
From: Kamil Dudka <kdudka@redhat.com>
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	[thread overview]
Message-ID: <1429277155-24607-1-git-send-email-kdudka@redhat.com> (raw)

This is already implemented <https://bugzilla.redhat.com/60870> in some
distributions of bash and tcsh <https://bugzilla.redhat.com/711066>.

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 <elf.h>
+#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 <https://bugzilla.redhat.com/60870>. */
+#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


             reply	other threads:[~2015-04-17 13:26 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-04-17 13:25 Kamil Dudka [this message]
2015-04-17 13:25 ` [PATCH 2/2] clear the heredoc list in case par_event() fails Kamil Dudka
2015-04-17 19:17   ` Peter Stephenson
2015-04-20  8:48     ` Kamil Dudka
2018-11-29 16:24     ` Kamil Dudka
2018-11-29 17:34       ` Peter Stephenson
2018-11-29 18:39         ` Bart Schaefer
2018-11-30  9:55           ` Peter Stephenson
2018-11-30 12:53         ` Kamil Dudka
2015-04-17 13:49 ` [PATCH 1/2] report bad ELF interpreter if it causes exec to fail Daniel Shahaf
2015-04-17 14:32   ` Kamil Dudka
2015-04-17 15:21 ` Oliver Kiddle
2015-04-17 15:59   ` Kamil Dudka
2015-04-17 19:59     ` Oliver Kiddle
2015-04-17 15:41 ` Philippe Troin
2015-04-17 15:51   ` Bart Schaefer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1429277155-24607-1-git-send-email-kdudka@redhat.com \
    --to=kdudka@redhat.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).