Commit ba00a0d4 authored by Tudor Bosman's avatar Tudor Bosman Committed by Chip Turner

Make ElfFile not crash on invalid ELF files

Summary:
@rkroll wants to run this on more than just fbcode binaries; there are x86 (not
x86_64) binaries in there and probably a lot of other junk. So, if you call
openNoThrow explicitly, you get a pretty error message in this case.

Test Plan: folly/experimental/symbolizer/test

Reviewed By: lucian@fb.com

Subscribers: rkroll, jhj, lesha, kma

FB internal diff: D1453758
parent 1274a688
...@@ -50,7 +50,11 @@ ElfFile::ElfFile(const char* name, bool readOnly) ...@@ -50,7 +50,11 @@ ElfFile::ElfFile(const char* name, bool readOnly)
void ElfFile::open(const char* name, bool readOnly) { void ElfFile::open(const char* name, bool readOnly) {
const char* msg = ""; const char* msg = "";
int r = openNoThrow(name, readOnly, &msg); int r = openNoThrow(name, readOnly, &msg);
folly::checkUnixError(r, msg); if (r == kSystemError) {
throwSystemError(msg);
} else {
CHECK_EQ(r, kSuccess) << msg;
}
} }
int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg) int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg)
...@@ -59,14 +63,14 @@ int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg) ...@@ -59,14 +63,14 @@ int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg)
fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR); fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR);
if (fd_ == -1) { if (fd_ == -1) {
if (msg) *msg = "open"; if (msg) *msg = "open";
return -1; return kSystemError;
} }
struct stat st; struct stat st;
int r = fstat(fd_, &st); int r = fstat(fd_, &st);
if (r == -1) { if (r == -1) {
if (msg) *msg = "fstat"; if (msg) *msg = "fstat";
return -1; return kSystemError;
} }
length_ = st.st_size; length_ = st.st_size;
...@@ -77,17 +81,21 @@ int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg) ...@@ -77,17 +81,21 @@ int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg)
file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0)); file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0));
if (file_ == MAP_FAILED) { if (file_ == MAP_FAILED) {
if (msg) *msg = "mmap"; if (msg) *msg = "mmap";
return -1; return kSystemError;
}
if (!init(msg)) {
errno = EINVAL;
return kInvalidElfFile;
} }
init();
return 0; return kSuccess;
} }
ElfFile::~ElfFile() { ElfFile::~ElfFile() {
destroy(); destroy();
} }
ElfFile::ElfFile(ElfFile&& other) ElfFile::ElfFile(ElfFile&& other) noexcept
: fd_(other.fd_), : fd_(other.fd_),
file_(other.file_), file_(other.file_),
length_(other.length_), length_(other.length_),
...@@ -125,22 +133,26 @@ void ElfFile::destroy() { ...@@ -125,22 +133,26 @@ void ElfFile::destroy() {
} }
} }
void ElfFile::init() { bool ElfFile::init(const char** msg) {
auto& elfHeader = this->elfHeader(); auto& elfHeader = this->elfHeader();
// Validate ELF magic numbers // Validate ELF magic numbers
FOLLY_SAFE_CHECK(elfHeader.e_ident[EI_MAG0] == ELFMAG0 && if (!(elfHeader.e_ident[EI_MAG0] == ELFMAG0 &&
elfHeader.e_ident[EI_MAG1] == ELFMAG1 && elfHeader.e_ident[EI_MAG1] == ELFMAG1 &&
elfHeader.e_ident[EI_MAG2] == ELFMAG2 && elfHeader.e_ident[EI_MAG2] == ELFMAG2 &&
elfHeader.e_ident[EI_MAG3] == ELFMAG3, elfHeader.e_ident[EI_MAG3] == ELFMAG3)) {
"invalid ELF magic"); if (msg) *msg = "invalid ELF magic";
return false;
}
// Validate ELF class (32/64 bits) // Validate ELF class (32/64 bits)
#define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS) #define EXPECTED_CLASS P1(ELFCLASS, __ELF_NATIVE_CLASS)
#define P1(a, b) P2(a, b) #define P1(a, b) P2(a, b)
#define P2(a, b) a ## b #define P2(a, b) a ## b
FOLLY_SAFE_CHECK(elfHeader.e_ident[EI_CLASS] == EXPECTED_CLASS, if (elfHeader.e_ident[EI_CLASS] != EXPECTED_CLASS) {
"invalid ELF class"); if (msg) *msg = "invalid ELF class";
return false;
}
#undef P1 #undef P1
#undef P2 #undef P2
#undef EXPECTED_CLASS #undef EXPECTED_CLASS
...@@ -153,24 +165,38 @@ void ElfFile::init() { ...@@ -153,24 +165,38 @@ void ElfFile::init() {
#else #else
# error Unsupported byte order # error Unsupported byte order
#endif #endif
FOLLY_SAFE_CHECK(elfHeader.e_ident[EI_DATA] == EXPECTED_ENCODING, if (elfHeader.e_ident[EI_DATA] != EXPECTED_ENCODING) {
"invalid ELF encoding"); if (msg) *msg = "invalid ELF encoding";
return false;
}
#undef EXPECTED_ENCODING #undef EXPECTED_ENCODING
// Validate ELF version (1) // Validate ELF version (1)
FOLLY_SAFE_CHECK(elfHeader.e_ident[EI_VERSION] == EV_CURRENT && if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT ||
elfHeader.e_version == EV_CURRENT, elfHeader.e_version != EV_CURRENT) {
"invalid ELF version"); if (msg) *msg = "invalid ELF version";
return false;
}
// We only support executable and shared object files // We only support executable and shared object files
FOLLY_SAFE_CHECK(elfHeader.e_type == ET_EXEC || elfHeader.e_type == ET_DYN, if (elfHeader.e_type != ET_EXEC && elfHeader.e_type != ET_DYN) {
"invalid ELF file type"); if (msg) *msg = "invalid ELF file type";
return false;
}
FOLLY_SAFE_CHECK(elfHeader.e_phnum != 0, "no program header!"); if (elfHeader.e_phnum == 0) {
FOLLY_SAFE_CHECK(elfHeader.e_phentsize == sizeof(ElfW(Phdr)), if (msg) *msg = "no program header!";
"invalid program header entry size"); return false;
FOLLY_SAFE_CHECK(elfHeader.e_shentsize == sizeof(ElfW(Shdr)), }
"invalid section header entry size");
if (elfHeader.e_phentsize != sizeof(ElfW(Phdr))) {
if (msg) *msg = "invalid program header entry size";
return false;
}
if (elfHeader.e_shentsize != sizeof(ElfW(Shdr))) {
if (msg) *msg = "invalid section header entry size";
}
const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff); const ElfW(Phdr)* programHeader = &at<ElfW(Phdr)>(elfHeader.e_phoff);
bool foundBase = false; bool foundBase = false;
...@@ -184,7 +210,12 @@ void ElfFile::init() { ...@@ -184,7 +210,12 @@ void ElfFile::init() {
} }
} }
FOLLY_SAFE_CHECK(foundBase, "could not find base address"); if (!foundBase) {
if (msg) *msg = "could not find base address";
return false;
}
return true;
} }
const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const { const ElfW(Shdr)* ElfFile::getSectionByIndex(size_t idx) const {
......
...@@ -49,8 +49,15 @@ class ElfFile { ...@@ -49,8 +49,15 @@ class ElfFile {
explicit ElfFile(const char* name, bool readOnly=true); explicit ElfFile(const char* name, bool readOnly=true);
// Open the ELF file. // Open the ELF file.
// Returns 0 on success, -1 (and sets errno) on failure and (if msg is not // Returns 0 on success, kSystemError (guaranteed to be -1) (and sets errno)
// NULL) sets *msg to a static string indicating what failed. // on IO error, kInvalidElfFile (and sets errno to EINVAL) for an invalid
// Elf file. On error, if msg is not NULL, sets *msg to a static string
// indicating what failed.
enum {
kSuccess = 0,
kSystemError = -1,
kInvalidElfFile = -2,
};
int openNoThrow(const char* name, bool readOnly=true, int openNoThrow(const char* name, bool readOnly=true,
const char** msg=nullptr) noexcept; const char** msg=nullptr) noexcept;
...@@ -59,7 +66,7 @@ class ElfFile { ...@@ -59,7 +66,7 @@ class ElfFile {
~ElfFile(); ~ElfFile();
ElfFile(ElfFile&& other); ElfFile(ElfFile&& other) noexcept;
ElfFile& operator=(ElfFile&& other); ElfFile& operator=(ElfFile&& other);
/** Retrieve the ELF header */ /** Retrieve the ELF header */
...@@ -185,7 +192,7 @@ class ElfFile { ...@@ -185,7 +192,7 @@ class ElfFile {
const ElfW(Shdr)* getSectionContainingAddress(ElfW(Addr) addr) const; const ElfW(Shdr)* getSectionContainingAddress(ElfW(Addr) addr) const;
private: private:
void init(); bool init(const char** msg);
void destroy(); void destroy();
ElfFile(const ElfFile&) = delete; ElfFile(const ElfFile&) = delete;
ElfFile& operator=(const ElfFile&) = delete; ElfFile& operator=(const ElfFile&) = delete;
......
...@@ -46,9 +46,13 @@ std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) { ...@@ -46,9 +46,13 @@ std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) {
} }
auto& f = slots_[n]; auto& f = slots_[n];
if (f->openNoThrow(path.data()) == -1) {
const char* msg = "";
int r = f->openNoThrow(path.data(), true, &msg);
if (r == ElfFile::kSystemError) {
return nullptr; return nullptr;
} }
FOLLY_SAFE_CHECK(r == ElfFile::kSuccess, msg);
map_[path] = n; map_[path] = n;
return f; return f;
...@@ -73,9 +77,12 @@ std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) { ...@@ -73,9 +77,12 @@ std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {
auto& path = entry->path; auto& path = entry->path;
// No negative caching // No negative caching
if (entry->file.openNoThrow(path.c_str()) == -1) { const char* msg = "";
int r = entry->file.openNoThrow(path.c_str(), true, &msg);
if (r == ElfFile::kSystemError) {
return nullptr; return nullptr;
} }
FOLLY_SAFE_CHECK(r == ElfFile::kSuccess, msg);
if (files_.size() == capacity_) { if (files_.size() == capacity_) {
auto& e = lruList_.front(); auto& e = lruList_.front();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment