mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
8ad4fcd0a8
PiperOrigin-RevId: 243775723 Change-Id: I5398ec23bd76be01c48c69bd4decb015a48386fc
530 lines
20 KiB
C++
530 lines
20 KiB
C++
// Copyright 2019 Google LLC. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "sandboxed_api/sandbox2/util/minielf.h"
|
|
|
|
#include <elf.h>
|
|
|
|
#include <cstddef>
|
|
#include <memory>
|
|
|
|
#include "absl/base/internal/endian.h"
|
|
#include "absl/strings/match.h"
|
|
#include "absl/strings/str_cat.h"
|
|
#include "sandboxed_api/sandbox2/util.h"
|
|
#include "sandboxed_api/sandbox2/util/strerror.h"
|
|
#include "sandboxed_api/util/raw_logging.h"
|
|
#include "sandboxed_api/util/canonical_errors.h"
|
|
#include "sandboxed_api/util/status.h"
|
|
#include "sandboxed_api/util/status_macros.h"
|
|
|
|
namespace sandbox2 {
|
|
|
|
constexpr int kElfHeaderSize =
|
|
sizeof(Elf64_Ehdr); // Maximum size for 64-bit binaries
|
|
|
|
constexpr char kElfMagic[] =
|
|
"\x7F"
|
|
"ELF";
|
|
|
|
constexpr int kEiClassOffset = 0x04;
|
|
constexpr int kEiClass64 = 2; // 64-bit binary
|
|
|
|
constexpr int kEiDataOffset = 0x05;
|
|
constexpr int kEiDataLittle = 1; // Little Endian
|
|
constexpr int kEiDataBig = 2; // Big Endian
|
|
|
|
constexpr int kEiVersionOffset = 0x06;
|
|
constexpr int kEvCurrent = 1; // ELF version
|
|
|
|
namespace {
|
|
|
|
// NOLINTNEXTLINE
|
|
::sapi::Status CheckedFSeek(FILE* f, long offset, int whence) {
|
|
if (fseek(f, offset, whence)) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("Fseek on ELF failed: ", StrError(errno)));
|
|
}
|
|
return ::sapi::OkStatus();
|
|
}
|
|
|
|
::sapi::Status CheckedFRead(void* dst, size_t size, size_t nmemb, FILE* f) {
|
|
if (fread(dst, size, nmemb, f) == nmemb) {
|
|
return ::sapi::OkStatus();
|
|
}
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("Reading ELF data failed: ", StrError(errno)));
|
|
}
|
|
|
|
::sapi::Status CheckedRead(std::string* s, FILE* f) {
|
|
return CheckedFRead(&(*s)[0], 1, s->size(), f);
|
|
}
|
|
|
|
absl::string_view ReadName(uint32_t offset, absl::string_view strtab) {
|
|
auto name = strtab.substr(offset);
|
|
return name.substr(0, name.find('\0'));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#define LOAD_MEMBER(data_struct, member, src) \
|
|
Load(&(data_struct).member, \
|
|
&src[offsetof(std::remove_reference<decltype(data_struct)>::type, \
|
|
member)])
|
|
|
|
class ElfParser {
|
|
public:
|
|
// Arbitrary cut-off values, so we can parse safely.
|
|
static constexpr int kMaxProgramHeaderEntries = 500;
|
|
static constexpr int kMaxSectionHeaderEntries = 500;
|
|
static constexpr size_t kMaxSectionSize = 100 * 1024 * 1024;
|
|
static constexpr size_t kMaxStrtabSize = 500 * 1024 * 1024;
|
|
static constexpr size_t kMaxLibPathSize = 1024;
|
|
static constexpr int kMaxSymbolEntries = 1 * 1000 * 1000;
|
|
static constexpr int kMaxDynamicEntries = 10000;
|
|
static constexpr size_t kMaxInterpreterSize = 1000;
|
|
|
|
ElfParser() = default;
|
|
::sapi::StatusOr<ElfFile> Parse(FILE* elf, uint32_t features);
|
|
|
|
private:
|
|
// Endianess support functions
|
|
uint16_t Load16(const void* src) {
|
|
return elf_little_ ? absl::little_endian::Load16(src)
|
|
: absl::big_endian::Load16(src);
|
|
}
|
|
uint32_t Load32(const void* src) {
|
|
return elf_little_ ? absl::little_endian::Load32(src)
|
|
: absl::big_endian::Load32(src);
|
|
}
|
|
uint64_t Load64(const void* src) {
|
|
return elf_little_ ? absl::little_endian::Load64(src)
|
|
: absl::big_endian::Load64(src);
|
|
}
|
|
template <size_t N>
|
|
void Load(unsigned char (*dst)[N], const void* src) {
|
|
memcpy(dst, src, N);
|
|
}
|
|
void Load(uint8_t* dst, const void* src) {
|
|
*dst = *reinterpret_cast<const char*>(src);
|
|
}
|
|
void Load(uint16_t* dst, const void* src) { *dst = Load16(src); }
|
|
void Load(uint32_t* dst, const void* src) { *dst = Load32(src); }
|
|
void Load(uint64_t* dst, const void* src) { *dst = Load64(src); }
|
|
void Load(int8_t* dst, const void* src) {
|
|
*dst = *reinterpret_cast<const char*>(src);
|
|
}
|
|
void Load(int16_t* dst, const void* src) { *dst = Load16(src); }
|
|
void Load(int32_t* dst, const void* src) { *dst = Load32(src); }
|
|
void Load(int64_t* dst, const void* src) { *dst = Load64(src); }
|
|
|
|
// Reads elf file size.
|
|
::sapi::Status ReadFileSize();
|
|
// Reads elf header.
|
|
::sapi::Status ReadFileHeader();
|
|
// Reads a single elf program header.
|
|
::sapi::StatusOr<Elf64_Phdr> ReadProgramHeader(absl::string_view src);
|
|
// Reads all elf program headers.
|
|
::sapi::Status ReadProgramHeaders();
|
|
// Reads a single elf section header.
|
|
::sapi::StatusOr<Elf64_Shdr> ReadSectionHeader(absl::string_view src);
|
|
// Reads all elf section headers.
|
|
::sapi::Status ReadSectionHeaders();
|
|
// Reads contents of an elf section.
|
|
::sapi::StatusOr<std::string> ReadSectionContents(int idx);
|
|
::sapi::StatusOr<std::string> ReadSectionContents(
|
|
const Elf64_Shdr& section_header);
|
|
// Reads all symbols from symtab section.
|
|
::sapi::Status ReadSymbolsFromSymtab(const Elf64_Shdr& symtab);
|
|
// Reads all imported libraries from dynamic section.
|
|
::sapi::Status ReadImportedLibrariesFromDynamic(const Elf64_Shdr& dynamic);
|
|
|
|
ElfFile result_;
|
|
FILE* elf_ = nullptr;
|
|
size_t file_size_ = 0;
|
|
bool elf_little_ = false;
|
|
Elf64_Ehdr file_header_;
|
|
std::vector<Elf64_Phdr> program_headers_;
|
|
std::vector<Elf64_Shdr> section_headers_;
|
|
|
|
int symbol_entries_read = 0;
|
|
int dynamic_entries_read = 0;
|
|
};
|
|
|
|
constexpr int ElfParser::kMaxProgramHeaderEntries;
|
|
constexpr int ElfParser::kMaxSectionHeaderEntries;
|
|
constexpr size_t ElfParser::kMaxSectionSize;
|
|
constexpr size_t ElfParser::kMaxStrtabSize;
|
|
constexpr size_t ElfParser::kMaxLibPathSize;
|
|
constexpr int ElfParser::kMaxSymbolEntries;
|
|
constexpr int ElfParser::kMaxDynamicEntries;
|
|
constexpr size_t ElfParser::kMaxInterpreterSize;
|
|
|
|
::sapi::Status ElfParser::ReadFileSize() {
|
|
fseek(elf_, 0, SEEK_END);
|
|
file_size_ = ftell(elf_);
|
|
if (file_size_ < kElfHeaderSize) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("file too small: ", file_size_, " bytes, at least ",
|
|
kElfHeaderSize, " bytes expected"));
|
|
}
|
|
return ::sapi::OkStatus();
|
|
}
|
|
|
|
::sapi::Status ElfParser::ReadFileHeader() {
|
|
std::string header(kElfHeaderSize, '\0');
|
|
SAPI_RETURN_IF_ERROR(CheckedFSeek(elf_, 0, SEEK_SET));
|
|
SAPI_RETURN_IF_ERROR(CheckedRead(&header, elf_));
|
|
|
|
if (!absl::StartsWith(header, kElfMagic)) {
|
|
return ::sapi::FailedPreconditionError("magic not found, not an ELF");
|
|
}
|
|
|
|
if (header[kEiClassOffset] != kEiClass64) {
|
|
return ::sapi::FailedPreconditionError("invalid ELF class");
|
|
}
|
|
const auto elf_data = header[kEiDataOffset];
|
|
elf_little_ = elf_data == kEiDataLittle;
|
|
if (!elf_little_ && elf_data != kEiDataBig) {
|
|
return ::sapi::FailedPreconditionError("invalid endianness");
|
|
}
|
|
|
|
if (header[kEiVersionOffset] != kEvCurrent) {
|
|
return ::sapi::FailedPreconditionError("invalid ELF version");
|
|
}
|
|
LOAD_MEMBER(file_header_, e_ident, header.data());
|
|
LOAD_MEMBER(file_header_, e_type, header.data());
|
|
LOAD_MEMBER(file_header_, e_machine, header.data());
|
|
LOAD_MEMBER(file_header_, e_version, header.data());
|
|
LOAD_MEMBER(file_header_, e_entry, header.data());
|
|
LOAD_MEMBER(file_header_, e_phoff, header.data());
|
|
LOAD_MEMBER(file_header_, e_shoff, header.data());
|
|
LOAD_MEMBER(file_header_, e_flags, header.data());
|
|
LOAD_MEMBER(file_header_, e_ehsize, header.data());
|
|
LOAD_MEMBER(file_header_, e_phentsize, header.data());
|
|
LOAD_MEMBER(file_header_, e_phnum, header.data());
|
|
LOAD_MEMBER(file_header_, e_shentsize, header.data());
|
|
LOAD_MEMBER(file_header_, e_shnum, header.data());
|
|
LOAD_MEMBER(file_header_, e_shstrndx, header.data());
|
|
return ::sapi::OkStatus();
|
|
}
|
|
|
|
::sapi::StatusOr<Elf64_Shdr> ElfParser::ReadSectionHeader(
|
|
absl::string_view src) {
|
|
if (src.size() < sizeof(Elf64_Shdr)) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("invalid section header data: got ", src.size(),
|
|
" bytes, ", sizeof(Elf64_Shdr), " bytes expected."));
|
|
}
|
|
Elf64_Shdr rv;
|
|
LOAD_MEMBER(rv, sh_name, src.data());
|
|
LOAD_MEMBER(rv, sh_type, src.data());
|
|
LOAD_MEMBER(rv, sh_flags, src.data());
|
|
LOAD_MEMBER(rv, sh_addr, src.data());
|
|
LOAD_MEMBER(rv, sh_offset, src.data());
|
|
LOAD_MEMBER(rv, sh_size, src.data());
|
|
LOAD_MEMBER(rv, sh_link, src.data());
|
|
LOAD_MEMBER(rv, sh_info, src.data());
|
|
LOAD_MEMBER(rv, sh_addralign, src.data());
|
|
LOAD_MEMBER(rv, sh_entsize, src.data());
|
|
return rv;
|
|
}
|
|
|
|
::sapi::Status ElfParser::ReadSectionHeaders() {
|
|
if (file_header_.e_shoff > file_size_) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("invalid section header offset: ", file_header_.e_shoff));
|
|
}
|
|
if (file_header_.e_shentsize != sizeof(Elf64_Shdr)) {
|
|
return ::sapi::FailedPreconditionError(absl::StrCat(
|
|
"section header entry size incorrect: ", file_header_.e_shentsize,
|
|
" bytes, ", sizeof(Elf64_Shdr), " expected."));
|
|
}
|
|
if (file_header_.e_shnum > kMaxSectionHeaderEntries) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("too many section header entries: ", file_header_.e_shnum,
|
|
" limit: ", kMaxSectionHeaderEntries));
|
|
}
|
|
std::string headers(file_header_.e_shentsize * file_header_.e_shnum, '\0');
|
|
SAPI_RETURN_IF_ERROR(CheckedFSeek(elf_, file_header_.e_shoff, SEEK_SET));
|
|
SAPI_RETURN_IF_ERROR(CheckedRead(&headers, elf_));
|
|
section_headers_.resize(file_header_.e_shnum);
|
|
absl::string_view src = headers;
|
|
for (int i = 0; i < file_header_.e_shnum; ++i) {
|
|
SAPI_ASSIGN_OR_RETURN(section_headers_[i], ReadSectionHeader(src));
|
|
src = src.substr(file_header_.e_shentsize);
|
|
}
|
|
return ::sapi::OkStatus();
|
|
}
|
|
|
|
::sapi::StatusOr<std::string> ElfParser::ReadSectionContents(int idx) {
|
|
if (idx < 0 || idx >= section_headers_.size()) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("invalid section header index: ", idx));
|
|
}
|
|
return ReadSectionContents(section_headers_.at(idx));
|
|
}
|
|
|
|
::sapi::StatusOr<std::string> ElfParser::ReadSectionContents(
|
|
const Elf64_Shdr& section_header) {
|
|
auto offset = section_header.sh_offset;
|
|
if (offset > file_size_) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("invalid section offset: ", offset));
|
|
}
|
|
auto size = section_header.sh_size;
|
|
if (size > kMaxSectionSize) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("section too big: ", size, " limit: ", kMaxSectionSize));
|
|
}
|
|
std::string rv(size, '\0');
|
|
SAPI_RETURN_IF_ERROR(CheckedFSeek(elf_, offset, SEEK_SET));
|
|
SAPI_RETURN_IF_ERROR(CheckedRead(&rv, elf_));
|
|
return rv;
|
|
}
|
|
|
|
::sapi::StatusOr<Elf64_Phdr> ElfParser::ReadProgramHeader(
|
|
absl::string_view src) {
|
|
if (src.size() < sizeof(Elf64_Phdr)) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("invalid program header data: got ", src.size(),
|
|
" bytes, ", sizeof(Elf64_Phdr), " bytes expected."));
|
|
}
|
|
Elf64_Phdr rv;
|
|
LOAD_MEMBER(rv, p_type, src.data());
|
|
LOAD_MEMBER(rv, p_flags, src.data());
|
|
LOAD_MEMBER(rv, p_offset, src.data());
|
|
LOAD_MEMBER(rv, p_vaddr, src.data());
|
|
LOAD_MEMBER(rv, p_paddr, src.data());
|
|
LOAD_MEMBER(rv, p_filesz, src.data());
|
|
LOAD_MEMBER(rv, p_memsz, src.data());
|
|
LOAD_MEMBER(rv, p_align, src.data());
|
|
return rv;
|
|
}
|
|
|
|
::sapi::Status ElfParser::ReadProgramHeaders() {
|
|
if (file_header_.e_phoff > file_size_) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("invalid program header offset: ", file_header_.e_phoff));
|
|
}
|
|
if (file_header_.e_phentsize != sizeof(Elf64_Phdr)) {
|
|
return ::sapi::FailedPreconditionError(absl::StrCat(
|
|
"section header entry size incorrect: ", file_header_.e_phentsize,
|
|
" bytes, ", sizeof(Elf64_Phdr), " expected."));
|
|
}
|
|
if (file_header_.e_phnum > kMaxProgramHeaderEntries) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("too many program header entries: ", file_header_.e_phnum,
|
|
" limit: ", kMaxProgramHeaderEntries));
|
|
}
|
|
std::string headers(file_header_.e_phentsize * file_header_.e_phnum, '\0');
|
|
SAPI_RETURN_IF_ERROR(CheckedFSeek(elf_, file_header_.e_phoff, SEEK_SET));
|
|
SAPI_RETURN_IF_ERROR(CheckedRead(&headers, elf_));
|
|
program_headers_.resize(file_header_.e_phnum);
|
|
absl::string_view src = headers;
|
|
for (int i = 0; i < file_header_.e_phnum; ++i) {
|
|
SAPI_ASSIGN_OR_RETURN(program_headers_[i], ReadProgramHeader(src));
|
|
src = src.substr(file_header_.e_phentsize);
|
|
}
|
|
return ::sapi::OkStatus();
|
|
}
|
|
|
|
::sapi::Status ElfParser::ReadSymbolsFromSymtab(const Elf64_Shdr& symtab) {
|
|
if (symtab.sh_type != SHT_SYMTAB) {
|
|
return ::sapi::FailedPreconditionError("invalid symtab type");
|
|
}
|
|
if (symtab.sh_entsize != sizeof(Elf64_Sym)) {
|
|
return ::sapi::InternalError(
|
|
absl::StrCat("invalid symbol entry size: ", symtab.sh_entsize));
|
|
}
|
|
if ((symtab.sh_size % symtab.sh_entsize) != 0) {
|
|
return ::sapi::InternalError(
|
|
absl::StrCat("invalid symbol table size: ", symtab.sh_size));
|
|
}
|
|
size_t symbol_entries = symtab.sh_size / symtab.sh_entsize;
|
|
if (symbol_entries > kMaxSymbolEntries - symbol_entries_read) {
|
|
return ::sapi::InternalError(
|
|
absl::StrCat("too many symbols: ", symbol_entries));
|
|
}
|
|
symbol_entries_read += symbol_entries;
|
|
if (symtab.sh_link >= section_headers_.size()) {
|
|
return ::sapi::InternalError(
|
|
absl::StrCat("invalid symtab's strtab reference: ", symtab.sh_link));
|
|
}
|
|
SAPI_RAW_VLOG(1, "Symbol table with %d entries found", symbol_entries);
|
|
SAPI_ASSIGN_OR_RETURN(std::string strtab, ReadSectionContents(symtab.sh_link));
|
|
SAPI_ASSIGN_OR_RETURN(std::string symbols, ReadSectionContents(symtab));
|
|
result_.symbols_.reserve(result_.symbols_.size() + symbol_entries);
|
|
for (absl::string_view src = symbols; !src.empty();
|
|
src = src.substr(symtab.sh_entsize)) {
|
|
Elf64_Sym symbol;
|
|
LOAD_MEMBER(symbol, st_name, src.data());
|
|
LOAD_MEMBER(symbol, st_info, src.data());
|
|
LOAD_MEMBER(symbol, st_other, src.data());
|
|
LOAD_MEMBER(symbol, st_shndx, src.data());
|
|
LOAD_MEMBER(symbol, st_value, src.data());
|
|
LOAD_MEMBER(symbol, st_size, src.data());
|
|
if (symbol.st_shndx == SHN_UNDEF) {
|
|
// External symbol, not supported.
|
|
continue;
|
|
}
|
|
if (symbol.st_shndx == SHN_ABS) {
|
|
// Absolute value, not supported.
|
|
continue;
|
|
}
|
|
if (symbol.st_shndx >= section_headers_.size()) {
|
|
return ::sapi::FailedPreconditionError(absl::StrCat(
|
|
"invalid symbol data: section index: ", symbol.st_shndx));
|
|
}
|
|
if (symbol.st_name >= strtab.size()) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("invalid name reference: REL", symbol.st_value));
|
|
}
|
|
result_.symbols_.push_back(
|
|
{symbol.st_value, std::string(ReadName(symbol.st_name, strtab))});
|
|
}
|
|
return ::sapi::OkStatus();
|
|
}
|
|
|
|
::sapi::Status ElfParser::ReadImportedLibrariesFromDynamic(
|
|
const Elf64_Shdr& dynamic) {
|
|
if (dynamic.sh_type != SHT_DYNAMIC) {
|
|
return ::sapi::FailedPreconditionError("invalid dynamic type");
|
|
}
|
|
if (dynamic.sh_entsize != sizeof(Elf64_Dyn)) {
|
|
return ::sapi::InternalError(
|
|
absl::StrCat("invalid dynamic entry size: ", dynamic.sh_entsize));
|
|
}
|
|
if ((dynamic.sh_size % dynamic.sh_entsize) != 0) {
|
|
return ::sapi::InternalError(
|
|
absl::StrCat("invalid dynamic table size: ", dynamic.sh_size));
|
|
}
|
|
size_t entries = dynamic.sh_size / dynamic.sh_entsize;
|
|
if (entries > kMaxDynamicEntries - dynamic_entries_read) {
|
|
return ::sapi::InternalError(
|
|
absl::StrCat("too many dynamic entries: ", entries));
|
|
}
|
|
dynamic_entries_read += entries;
|
|
if (dynamic.sh_link >= section_headers_.size()) {
|
|
return ::sapi::InternalError(
|
|
absl::StrCat("invalid dynamic's strtab reference: ", dynamic.sh_link));
|
|
}
|
|
SAPI_RAW_VLOG(1, "Dynamic section with %d entries found", entries);
|
|
// strtab may be shared with symbols and therefore huge
|
|
const auto& strtab_section = section_headers_.at(dynamic.sh_link);
|
|
if (strtab_section.sh_offset > file_size_) {
|
|
return ::sapi::FailedPreconditionError(absl::StrCat(
|
|
"invalid symtab's strtab section offset: ", strtab_section.sh_offset));
|
|
}
|
|
if (strtab_section.sh_size >= kMaxStrtabSize ||
|
|
strtab_section.sh_size >= file_size_ ||
|
|
strtab_section.sh_offset >= file_size_ - strtab_section.sh_size) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("symtab's strtab too big: ", strtab_section.sh_size));
|
|
}
|
|
auto strtab_end = strtab_section.sh_offset + strtab_section.sh_size;
|
|
SAPI_ASSIGN_OR_RETURN(std::string dynamic_entries, ReadSectionContents(dynamic));
|
|
for (absl::string_view src = dynamic_entries; !src.empty();
|
|
src = src.substr(dynamic.sh_entsize)) {
|
|
Elf64_Dyn dyn;
|
|
LOAD_MEMBER(dyn, d_tag, src.data());
|
|
LOAD_MEMBER(dyn, d_un.d_val, src.data());
|
|
if (dyn.d_tag != DT_NEEDED) {
|
|
continue;
|
|
}
|
|
if (dyn.d_un.d_val >= strtab_section.sh_size) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("invalid name reference"));
|
|
}
|
|
auto offset = strtab_section.sh_offset + dyn.d_un.d_val;
|
|
SAPI_RETURN_IF_ERROR(CheckedFSeek(elf_, offset, SEEK_SET));
|
|
std::string path(std::min(kMaxLibPathSize, strtab_end - offset), '\0');
|
|
size_t size = fread(&path[0], 1, path.size(), elf_);
|
|
path.resize(size);
|
|
result_.imported_libraries_.push_back(path.substr(0, path.find('\0')));
|
|
}
|
|
return ::sapi::OkStatus();
|
|
}
|
|
|
|
::sapi::StatusOr<ElfFile> ElfParser::Parse(FILE* elf, uint32_t features) {
|
|
elf_ = elf;
|
|
// Basic sanity check.
|
|
if (features & ~(ElfFile::kAll)) {
|
|
return ::sapi::InvalidArgumentError("Unknown feature flags specified");
|
|
}
|
|
SAPI_RETURN_IF_ERROR(ReadFileSize());
|
|
SAPI_RETURN_IF_ERROR(ReadFileHeader());
|
|
switch (file_header_.e_type) {
|
|
case ET_EXEC:
|
|
result_.position_independent_ = false;
|
|
break;
|
|
case ET_DYN:
|
|
result_.position_independent_ = true;
|
|
break;
|
|
default:
|
|
return ::sapi::FailedPreconditionError("not an executable: ");
|
|
}
|
|
if (features & ElfFile::kGetInterpreter) {
|
|
SAPI_RETURN_IF_ERROR(ReadProgramHeaders());
|
|
std::string interpreter;
|
|
auto it = std::find_if(
|
|
program_headers_.begin(), program_headers_.end(),
|
|
[](const Elf64_Phdr& hdr) { return hdr.p_type == PT_INTERP; });
|
|
// No interpreter usually means that the executable was statically linked.
|
|
if (it != program_headers_.end()) {
|
|
if (it->p_filesz > kMaxInterpreterSize) {
|
|
return ::sapi::FailedPreconditionError(
|
|
absl::StrCat("program interpeter path too long: ", it->p_filesz));
|
|
}
|
|
SAPI_RETURN_IF_ERROR(CheckedFSeek(elf, it->p_offset, SEEK_SET));
|
|
interpreter.resize(it->p_filesz, '\0');
|
|
SAPI_RETURN_IF_ERROR(CheckedRead(&interpreter, elf));
|
|
auto first_nul = interpreter.find_first_of('\0');
|
|
if (first_nul != std::string::npos) {
|
|
interpreter.erase(first_nul);
|
|
}
|
|
}
|
|
result_.interpreter_ = std::move(interpreter);
|
|
}
|
|
|
|
if (features & (ElfFile::kLoadSymbols | ElfFile::kLoadImportedLibraries)) {
|
|
SAPI_RETURN_IF_ERROR(ReadSectionHeaders());
|
|
for (const auto& hdr : section_headers_) {
|
|
if (hdr.sh_type == SHT_SYMTAB && features & ElfFile::kLoadSymbols) {
|
|
SAPI_RETURN_IF_ERROR(ReadSymbolsFromSymtab(hdr));
|
|
}
|
|
if (hdr.sh_type == SHT_DYNAMIC &&
|
|
features & ElfFile::kLoadImportedLibraries) {
|
|
SAPI_RETURN_IF_ERROR(ReadImportedLibrariesFromDynamic(hdr));
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::move(result_);
|
|
}
|
|
|
|
::sapi::StatusOr<ElfFile> ElfFile::ParseFromFile(const std::string& filename,
|
|
uint32_t features) {
|
|
std::unique_ptr<FILE, void (*)(FILE*)> elf{fopen(filename.c_str(), "r"),
|
|
[](FILE* f) { fclose(f); }};
|
|
if (!elf) {
|
|
return ::sapi::UnknownError(
|
|
absl::StrCat("cannot open file: ", filename, ": ", StrError(errno)));
|
|
}
|
|
|
|
return ElfParser().Parse(elf.get(), features);
|
|
}
|
|
|
|
} // namespace sandbox2
|