blob: 805e0dd1ca1cfcbe655a18c17d3dc773e8ad8805 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "symbols.h"
#include <elf.h>
#include <lib/zircon-internal/align.h>
#include <lib/zx/vmar.h>
#include <zircon/status.h>
#include "restricted_symbols.h"
namespace {
const uint8_t kElfMagic[] = {0x7f, 'E', 'L', 'F'};
// Holds a string table that has been mapped into memory.
class MappedStringsTable {
public:
// Maps the table located at |offset| of |len| in |driver_vmo|.
static zx::result<MappedStringsTable> Create(zx::vmo& driver_vmo, uint64_t offset, uint64_t len) {
const size_t kPageSize = zx_system_get_page_size();
// Mapping only works on page-aligned values.
auto mapped_offset = ZX_ROUNDDOWN(offset, kPageSize);
auto mapped_size = ZX_ROUNDUP(len + (offset - mapped_offset), kPageSize);
zx_vaddr_t mapped_addr;
zx_status_t status = zx::vmar::root_self()->map(ZX_VM_PERM_READ, 0, driver_vmo, mapped_offset,
mapped_size, &mapped_addr);
if (status != ZX_OK) {
return zx::error(status);
}
const char* table = reinterpret_cast<char*>(mapped_addr + (offset - mapped_offset));
return zx::ok(MappedStringsTable(table, mapped_addr, mapped_size));
}
~MappedStringsTable() {
if (!ptr_) {
return;
}
[[maybe_unused]] zx_status_t status = zx::vmar::root_self()->unmap(mapped_addr_, mapped_size_);
ptr_ = nullptr;
mapped_addr_ = 0;
mapped_size_ = 0;
}
// Disallow copying.
MappedStringsTable(const MappedStringsTable&) = delete;
MappedStringsTable& operator=(const MappedStringsTable&) = delete;
MappedStringsTable(MappedStringsTable&& other)
: MappedStringsTable(other.ptr_, other.mapped_addr_, other.mapped_size_) {
other.ptr_ = nullptr;
other.mapped_addr_ = 0;
other.mapped_size_ = 0;
}
MappedStringsTable& operator=(MappedStringsTable&& other) {
ptr_ = other.ptr_;
mapped_addr_ = other.mapped_addr_;
mapped_size_ = other.mapped_size_;
other.ptr_ = nullptr;
other.mapped_addr_ = 0;
other.mapped_size_ = 0;
return *this;
}
// Returns the pointer to the mapped table.
const char* ptr() { return ptr_; }
private:
MappedStringsTable(const char* ptr, uint64_t mapped_addr, uint64_t mapped_size)
: ptr_(ptr), mapped_addr_(mapped_addr), mapped_size_(mapped_size) {}
const char* ptr_ = nullptr;
uint64_t mapped_addr_ = 0;
uint64_t mapped_size_ = 0;
};
zx::result<Elf64_Ehdr> GetElfHeader(zx::vmo& driver_vmo) {
Elf64_Ehdr elf_header;
zx_status_t status = driver_vmo.read(&elf_header, 0, sizeof(elf_header));
if (status != ZX_OK) {
return zx::error(status);
}
if (memcmp(elf_header.e_ident, kElfMagic, sizeof(kElfMagic)) != 0) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
if (elf_header.e_shoff == 0) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
return zx::ok(elf_header);
}
// Returns the section header located at |section_index| in the section header table.
zx::result<Elf64_Shdr> GetSectionHeader(zx::vmo& driver_vmo, const Elf64_Ehdr& elf_header,
uint32_t section_index) {
Elf64_Shdr section_header;
// The start of the section header table is located at |e_shoff| in the driver vmo.
uint64_t vmo_offset = elf_header.e_shoff + (section_index * sizeof(section_header));
zx_status_t status = driver_vmo.read(&section_header, vmo_offset, sizeof(section_header));
if (status != ZX_OK) {
return zx::error(status);
}
return zx::ok(section_header);
}
// Returns the section header matching |header_type|, or ZX_ERR_NOT_FOUND if no match is found.
zx::result<Elf64_Shdr> FindSectionHeader(zx::vmo& driver_vmo, const Elf64_Ehdr& elf_header,
Elf32_Word header_type) {
// |e_shnum| holds the number of section header table entries.
for (uint16_t i = 0; i < elf_header.e_shnum; i++) {
auto section_header = GetSectionHeader(driver_vmo, elf_header, i);
if (section_header->sh_type == SHT_DYNSYM) {
return section_header;
}
}
return zx::error(ZX_ERR_NOT_FOUND);
}
zx::result<std::string> GetRelativeUrl(std::string_view url) {
constexpr std::string_view kPrefix = "#meta/";
size_t prefix_start_idx = url.find(kPrefix);
if (prefix_start_idx == std::string::npos) {
return zx::error(ZX_ERR_NOT_FOUND);
}
return zx::ok(url.substr(prefix_start_idx));
}
} // namespace
namespace driver_symbols {
zx::result<std::vector<std::string>> FindRestrictedSymbols(zx::vmo& driver_vmo,
std::string_view driver_url) {
auto relative_url = GetRelativeUrl(driver_url);
if (relative_url.is_error()) {
return relative_url.take_error();
}
zx::result<Elf64_Ehdr> elf_header = GetElfHeader(driver_vmo);
if (elf_header.is_error()) {
return elf_header.take_error();
}
// Find the section header for the dynamic symbols table.
zx::result<Elf64_Shdr> dynsym_header = FindSectionHeader(driver_vmo, *elf_header, SHT_DYNSYM);
if (dynsym_header.is_error()) {
return dynsym_header.take_error();
}
// Find the section header for the dynamic symbols string table.
if (dynsym_header->sh_link == 0) {
return zx::error(ZX_ERR_NOT_FOUND);
}
auto dynsym_strings_header = GetSectionHeader(driver_vmo, *elf_header, dynsym_header->sh_link);
if (dynsym_strings_header.is_error()) {
return dynsym_strings_header.take_error();
}
// Map in the dynamic symbols string table.
auto dynsym_strings_table = MappedStringsTable::Create(
driver_vmo, dynsym_strings_header->sh_offset, dynsym_strings_header->sh_size);
if (dynsym_strings_table.is_error()) {
return dynsym_strings_table.take_error();
}
// Iterate through the dynamic symbols table and look for restricted symbols.
std::vector<std::string> matches;
uint64_t symbols_start = dynsym_header->sh_offset;
uint64_t symbols_end = symbols_start + dynsym_header->sh_size;
for (uint64_t offset = symbols_start; offset < symbols_end; offset += sizeof(Elf64_Sym)) {
Elf64_Sym symbol;
zx_status_t status = driver_vmo.read(&symbol, offset, sizeof(symbol));
if (status != ZX_OK) {
return zx::error(status);
}
if (symbol.st_name == 0) {
// It's valid for a symbol to not have a name.
continue;
}
// |st_name| is the index into the dynamic symbols string table.
const char* name = dynsym_strings_table->ptr() + symbol.st_name;
if (kRestrictedLibcSymbols.find(name) != kRestrictedLibcSymbols.end()) {
matches.push_back(name);
} else if (kRestrictedDriverRuntimeSymbols.find(name) !=
kRestrictedDriverRuntimeSymbols.end()) {
if (kRestrictedDriverRuntimeSymbolsDriversAllowlist.find(*relative_url) ==
kRestrictedDriverRuntimeSymbolsDriversAllowlist.end()) {
matches.push_back(name);
}
} else if (kCreateThreadSymbols.find(name) != kCreateThreadSymbols.end()) {
if (kCreateThreadSymbolsDriversAllowlist.find(*relative_url) ==
kCreateThreadSymbolsDriversAllowlist.end()) {
matches.push_back(name);
}
}
}
return zx::ok(matches);
}
} // namespace driver_symbols