Fix crash when trying to load invalid ELF file.

Bug: http://b/22047255
Bug: http://b/22091640
Change-Id: I6c51cff43287a6ac4b25fa9ce6a6fc3d232fd047
diff --git a/linker/linker.cpp b/linker/linker.cpp
index e147a13..e1b8ca9 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1293,7 +1293,7 @@
   }
 
   // Read the ELF header and load the segments.
-  ElfReader elf_reader(realpath.c_str(), fd, file_offset);
+  ElfReader elf_reader(realpath.c_str(), fd, file_offset, file_stat.st_size);
   if (!elf_reader.Load(extinfo)) {
     return nullptr;
   }
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index f586b08..30118e3 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -133,8 +133,8 @@
                                       MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \
                                       MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE))
 
-ElfReader::ElfReader(const char* name, int fd, off64_t file_offset)
-    : name_(name), fd_(fd), file_offset_(file_offset),
+ElfReader::ElfReader(const char* name, int fd, off64_t file_offset, off64_t file_size)
+    : name_(name), fd_(fd), file_offset_(file_offset), file_size_(file_size),
       phdr_num_(0), phdr_mmap_(nullptr), phdr_table_(nullptr), phdr_size_(0),
       load_start_(nullptr), load_size_(0), load_bias_(0),
       loaded_phdr_(nullptr) {
@@ -377,6 +377,20 @@
     ElfW(Addr) file_page_start = PAGE_START(file_start);
     ElfW(Addr) file_length = file_end - file_page_start;
 
+    if (file_size_ <= 0) {
+      DL_ERR("\"%s\" invalid file size: %" PRId64, name_, file_size_);
+      return false;
+    }
+
+    if (file_end >= static_cast<size_t>(file_size_)) {
+      DL_ERR("invalid ELF file \"%s\" load segment[%zd]:"
+          " p_offset (%p) + p_filesz (%p) ( = %p) past end of file (0x%" PRIx64 ")",
+          name_, i, reinterpret_cast<void*>(phdr->p_offset),
+          reinterpret_cast<void*>(phdr->p_filesz),
+          reinterpret_cast<void*>(file_end), file_size_);
+      return false;
+    }
+
     if (file_length != 0) {
       void* seg_addr = mmap64(reinterpret_cast<void*>(seg_page_start),
                             file_length,
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 50f2117..3affa66 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -39,7 +39,7 @@
 
 class ElfReader {
  public:
-  ElfReader(const char* name, int fd, off64_t file_offset);
+  ElfReader(const char* name, int fd, off64_t file_offset, off64_t file_size);
   ~ElfReader();
 
   bool Load(const android_dlextinfo* extinfo);
@@ -62,6 +62,7 @@
   const char* name_;
   int fd_;
   off64_t file_offset_;
+  off64_t file_size_;
 
   ElfW(Ehdr) header_;
   size_t phdr_num_;