Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

[PATCH 1/2] report bad ELF interpreter if it causes exec to fail



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



Messages sorted by: Reverse Date, Date, Thread, Author