Snap for 9849129 from e8035c3f686db07475eb947494e2d5765c33c958 to udc-release

Change-Id: Ie2564850182a85b32e2d5ede910715565e3d1484
diff --git a/android/relinterp.c b/android/relinterp.c
index 5fd8ea9..c900e79 100644
--- a/android/relinterp.c
+++ b/android/relinterp.c
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#define SYSCALL_NO_TLS 1
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -125,6 +126,10 @@
   return ri_syscall(SYS_mprotect, addr, len, prot);
 }
 
+static ssize_t ri_pread(int fd, void* buf, size_t size, off_t ofs) {
+  return ri_syscall(SYS_pread, fd, buf, size, __SYSCALL_LL_PRW(ofs));
+}
+
 static size_t ri_strlen(const char* src) {
   for (size_t len = 0;; ++len) {
     if (src[len] == '\0') return len;
@@ -647,7 +652,7 @@
   if ((size_t)result >= len) fatal("realpath of %s too long", orig_path);
 }
 
-static int open_loader(const char* path, OpenedLoader* loader) {
+static int open_loader(const ExeInfo* exe, const char* path, OpenedLoader* loader) {
   debug("trying to open '%s'", path);
   loader->fd = ri_open(path, O_RDONLY, 0);
   if (loader->fd < 0) {
@@ -655,12 +660,43 @@
     return -1;
   }
 
+  ElfW(Ehdr) hdr;
+  ssize_t l = ri_pread(loader->fd, &hdr, sizeof(hdr), 0);
+  if (l < 0) {
+    debug("reading elf header from %s failed: %s", path, ri_strerror(g_errno));
+    return -1;
+  }
+  if (l != sizeof(hdr)) {
+    debug("file %s too short to contain elf header", path);
+    return -1;
+  }
+
+  if (hdr.e_ident[0] != ELFMAG0 ||
+      hdr.e_ident[1] != ELFMAG1 ||
+      hdr.e_ident[2] != ELFMAG2 ||
+      hdr.e_ident[3] != ELFMAG3) {
+    debug("file %s is not an elf file", path);
+    return -1;
+  }
+
+  if (hdr.e_machine != exe->ehdr->e_machine) {
+    debug("incorrect elf machine for loader %s, expected %d got %d",
+          path, exe->ehdr->e_machine, hdr.e_machine);
+    return -1;
+  }
+
+  if (hdr.e_ident[EI_CLASS] != exe->ehdr->e_ident[EI_CLASS]) {
+    debug("incorrect elf class for loader %s, expected %d got %d",
+          path, exe->ehdr->e_ident[EI_CLASS], hdr.e_ident[EI_CLASS]);
+    return -1;
+  }
+
   realpath_fd(loader->fd, path, loader->path, sizeof(loader->path));
 
   return 0;
 }
 
-static int open_rel_loader(const char* dir, const char* rel, OpenedLoader* loader) {
+static int open_rel_loader(const ExeInfo* exe, const char* dir, const char* rel, OpenedLoader* loader) {
   char buf[PATH_MAX];
 
   size_t dir_len = ri_strlen(dir);
@@ -680,7 +716,7 @@
   }
   ri_strcat(buf, rel);
 
-  return open_loader(buf, loader);
+  return open_loader(exe, buf, loader);
 }
 
 static void get_origin(char* buf, size_t buf_len) {
@@ -693,7 +729,7 @@
   ri_dirname(buf);
 }
 
-static int search_path_list_for_loader(const char* loader_rel_path, const char* search_path,
+static int search_path_list_for_loader(const ExeInfo* exe, const char* loader_rel_path, const char* search_path,
                                        const char* search_path_name, bool expand_origin, OpenedLoader *loader) {
   char origin_buf[PATH_MAX];
   char* origin = NULL;
@@ -743,11 +779,12 @@
 
       ri_strcat(buf, origin);
       ri_strcat(buf, d+s);
-      debug("trying loader %s at %s", loader_rel_path, buf);
     } else {
       ri_strcpy(buf, search_path_entry);
     }
-    if (!open_rel_loader(buf, loader_rel_path, loader)) {
+    debug("trying loader %s at %s", loader_rel_path, buf);
+    if (!open_rel_loader(exe, buf, loader_rel_path, loader)) {
+      debug("opened loader %s at %s", loader_rel_path, buf);
       return 0;
     }
   }
@@ -759,14 +796,14 @@
   const char* loader_rel_path = LOADER_PATH;
 
   if (loader_rel_path[0] == '/') {
-    return open_loader(loader_rel_path, loader);
+    return open_loader(exe, loader_rel_path, loader);
   }
 
   if (exe->secure) {
     fatal("relinterp not supported for secure executables");
   }
 
-  if (!search_path_list_for_loader(loader_rel_path, ld_library_path, "LD_LIBRARY_PATH", false, loader)) {
+  if (!search_path_list_for_loader(exe, loader_rel_path, ld_library_path, "LD_LIBRARY_PATH", false, loader)) {
     return 0;
   }
 
@@ -774,10 +811,10 @@
     // If no DT_RUNPATH search relative to the exe.
     char origin[PATH_MAX];
     get_origin(origin, sizeof(origin));
-    return open_rel_loader(origin, loader_rel_path, loader);
+    return open_rel_loader(exe, origin, loader_rel_path, loader);
   }
 
-  if (!search_path_list_for_loader(loader_rel_path, exe->search_paths, "rpath", true, loader)) {
+  if (!search_path_list_for_loader(exe, loader_rel_path, exe->search_paths, "rpath", true, loader)) {
     return 0;
   }
 
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 24fe47c..d055bd2 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -65,6 +65,8 @@
 	size_t *dynv;
 	struct dso *next, *prev;
 
+	int elfmachine;
+	int elfclass;
 	Phdr *phdr;
 	int phnum;
 	size_t phentsize;
@@ -644,6 +646,19 @@
 	}
 }
 
+static int verify_elf_magic(const Ehdr* eh) {
+	return eh->e_ident[0] == ELFMAG0 &&
+		eh->e_ident[1] == ELFMAG1 &&
+		eh->e_ident[2] == ELFMAG2 &&
+		eh->e_ident[3] == ELFMAG3;
+}
+
+/* Verifies that an elf header's machine and class match the loader */
+static int verify_elf_arch(const Ehdr* eh) {
+	return eh->e_machine == ldso.elfmachine &&
+		eh->e_ident[EI_CLASS] == ldso.elfclass;
+}
+
 static void *map_library(int fd, struct dso *dso)
 {
 	Ehdr buf[(896+sizeof(Ehdr))/sizeof(Ehdr)];
@@ -666,6 +681,10 @@
 	if (l<0) return 0;
 	if (l<sizeof *eh || (eh->e_type != ET_DYN && eh->e_type != ET_EXEC))
 		goto noexec;
+	if (!verify_elf_magic(eh)) goto noexec;
+	if (!verify_elf_arch(eh)) goto noexec;
+	dso->elfmachine = eh->e_machine;
+	dso->elfclass = eh->e_ident[EI_CLASS];
 	phsize = eh->e_phentsize * eh->e_phnum;
 	if (phsize > sizeof buf - sizeof *eh) {
 		allocated_buf = malloc(phsize);
@@ -830,29 +849,53 @@
 	return 0;
 }
 
-static int path_open(const char *name, const char *s, char *buf, size_t buf_size)
+static int path_open_library(const char *name, const char *s, char *buf, size_t buf_size)
 {
 	size_t l;
 	int fd;
+	const char *p;
 	for (;;) {
 		s += strspn(s, ":\n");
+		p = s;
 		l = strcspn(s, ":\n");
 		if (l-1 >= INT_MAX) return -1;
-		if (snprintf(buf, buf_size, "%.*s/%s", (int)l, s, name) < buf_size) {
-			if ((fd = open(buf, O_RDONLY|O_CLOEXEC))>=0) return fd;
-			switch (errno) {
-			case ENOENT:
-			case ENOTDIR:
-			case EACCES:
-			case ENAMETOOLONG:
-				break;
-			default:
-				/* Any negative value but -1 will inhibit
-				 * futher path search. */
+		s += l;
+		if (snprintf(buf, buf_size, "%.*s/%s", (int)l, p, name) < buf_size) {
+			fd = open(buf, O_RDONLY|O_CLOEXEC);
+			if (fd < 0) {
+				switch (errno) {
+				case ENOENT:
+				case ENOTDIR:
+				case EACCES:
+				case ENAMETOOLONG:
+					/* Keep searching in path list. */
+					continue;
+				default:
+					/* Any negative value but -1 will
+					 * inhibit further path search in
+					 * load_library. */
+					return -2;
+				}
+			}
+			Ehdr eh;
+			ssize_t n = pread(fd, &eh, sizeof eh, 0);
+			/* If the elf file is invalid return -2 to inhibit
+			 * further path search in load_library. */
+			if (n < 0 ||
+			    n != sizeof eh ||
+			    !verify_elf_magic(&eh)) {
+				close(fd);
 				return -2;
 			}
+			/* If the elf file has a valid header but is for the
+			 * wrong architecture ignore it and keep searching the
+			 * path list. */
+			if (!verify_elf_arch(&eh)) {
+				close(fd);
+				continue;
+			}
+			return fd;
 		}
-		s += l;
 	}
 }
 
@@ -1076,12 +1119,12 @@
 		}
 		if (strlen(name) > NAME_MAX) return 0;
 		fd = -1;
-		if (env_path) fd = path_open(name, env_path, buf, sizeof buf);
+		if (env_path) fd = path_open_library(name, env_path, buf, sizeof buf);
 		for (p=needed_by; fd == -1 && p; p=p->needed_by) {
 			if (fixup_rpath(p, buf, sizeof buf) < 0)
 				fd = -2; /* Inhibit further search. */
 			if (p->rpath)
-				fd = path_open(name, p->rpath, buf, sizeof buf);
+				fd = path_open_library(name, p->rpath, buf, sizeof buf);
 		}
 		if (fd == -1) {
 			if (!sys_path) {
@@ -1120,7 +1163,7 @@
 				}
 			}
 			if (!sys_path) sys_path = "/lib:/usr/local/lib:/usr/lib";
-			fd = path_open(name, sys_path, buf, sizeof buf);
+			fd = path_open_library(name, sys_path, buf, sizeof buf);
 		}
 		pathname = buf;
 	}
@@ -1694,6 +1737,8 @@
 	ldso.phnum = ehdr->e_phnum;
 	ldso.phdr = laddr(&ldso, ehdr->e_phoff);
 	ldso.phentsize = ehdr->e_phentsize;
+	ldso.elfmachine = ehdr->e_machine;
+	ldso.elfclass = ehdr->e_ident[EI_CLASS];
 	kernel_mapped_dso(&ldso);
 	decode_dyn(&ldso);
 
diff --git a/src/misc/getopt.c b/src/misc/getopt.c
index c3f6699..f8df7ae 100644
--- a/src/misc/getopt.c
+++ b/src/misc/getopt.c
@@ -25,7 +25,7 @@
 	FUNLOCK(f);
 }
 
-int getopt(int argc, char * const argv[], const char *optstring)
+int __posix_getopt(int argc, char * const argv[], const char *optstring)
 {
 	int i;
 	wchar_t c, d;
@@ -101,5 +101,3 @@
 	}
 	return c;
 }
-
-weak_alias(getopt, __posix_getopt);
diff --git a/src/misc/getopt_long.c b/src/misc/getopt_long.c
index 6949ab1..117d1a1 100644
--- a/src/misc/getopt_long.c
+++ b/src/misc/getopt_long.c
@@ -9,6 +9,8 @@
 
 extern int __optpos, __optreset;
 
+int __posix_getopt(int argc, char * const argv[], const char *optstring);
+
 static void permute(char *const *argv, int dest, int src)
 {
 	char **av = (char **)argv;
@@ -134,7 +136,7 @@
 			return '?';
 		}
 	}
-	return getopt(argc, argv, optstring);
+	return __posix_getopt(argc, argv, optstring);
 }
 
 int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx)
@@ -146,3 +148,20 @@
 {
 	return __getopt_long(argc, argv, optstring, longopts, idx, 1);
 }
+
+/* ANDROID CHANGE: implement getopt via getopt_long to continue parsing options
+ * after the first non-option argument to match the user visible behavior of
+ * glibc.
+ */
+int getopt(int argc, char * const argv[], const char *optstring)
+{
+	static int posixly_correct = -1;
+
+	if (posixly_correct == -1 || __optreset)
+		posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+
+	if (posixly_correct)
+		return __posix_getopt(argc, argv, optstring);
+	else
+		return __getopt_long(argc, argv, optstring, NULL, NULL, 0);
+}