Merge changes from topic "revert-2709995-VVPYYBKIHY" into main am: 73a867a203
am: 3f54874625
am: 76a20d12f3
Original change: https://android-review.googlesource.com/c/platform/bionic/+/2862867 Change-Id: Iaeda56f0cb0a6f3fc21dcb10f26bc3fb5f78e647 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
cd0c908065
22 changed files with 20 additions and 870 deletions
|
@ -44,7 +44,7 @@ extern "C" int __set_tid_address(int* tid_address);
|
|||
// Declared in "private/bionic_ssp.h".
|
||||
uintptr_t __stack_chk_guard = 0;
|
||||
|
||||
BIONIC_USED_BEFORE_LINKER_RELOCATES static pthread_internal_t main_thread;
|
||||
static pthread_internal_t main_thread;
|
||||
|
||||
// Setup for the main thread. For dynamic executables, this is called by the
|
||||
// linker _before_ libc is mapped in memory. This means that all writes to
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include <sys/hwprobe.h>
|
||||
#include <sys/ifunc.h>
|
||||
|
||||
#include "bionic/macros.h"
|
||||
#include "private/bionic_auxv.h"
|
||||
|
||||
// This code is called in the linker before it has been relocated, so minimize calls into other
|
||||
|
@ -41,8 +40,8 @@
|
|||
ElfW(Addr) __bionic_call_ifunc_resolver(ElfW(Addr) resolver_addr) {
|
||||
#if defined(__aarch64__)
|
||||
typedef ElfW(Addr) (*ifunc_resolver_t)(uint64_t, __ifunc_arg_t*);
|
||||
BIONIC_USED_BEFORE_LINKER_RELOCATES static __ifunc_arg_t arg;
|
||||
BIONIC_USED_BEFORE_LINKER_RELOCATES static bool initialized = false;
|
||||
static __ifunc_arg_t arg;
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
arg._size = sizeof(__ifunc_arg_t);
|
||||
|
|
|
@ -410,7 +410,6 @@ __attribute__((no_sanitize("hwaddress", "memtag"))) void __libc_init_mte(
|
|||
// We did not enable MTE, so we do not need to arm the upgrade timer.
|
||||
__libc_shared_globals()->heap_tagging_upgrade_timer_sec = 0;
|
||||
}
|
||||
|
||||
#else // __aarch64__
|
||||
void __libc_init_mte(const memtag_dynamic_entries_t*, const void*, size_t, uintptr_t, void*) {}
|
||||
#endif // __aarch64__
|
||||
|
@ -505,6 +504,6 @@ extern "C" void android_set_application_target_sdk_version(int target) {
|
|||
// compiled with -ffreestanding to avoid implicit string.h function calls. (It shouldn't strictly
|
||||
// be necessary, though.)
|
||||
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals() {
|
||||
BIONIC_USED_BEFORE_LINKER_RELOCATES static libc_shared_globals globals;
|
||||
static libc_shared_globals globals;
|
||||
return &globals;
|
||||
}
|
||||
|
|
|
@ -97,17 +97,3 @@ template <typename T>
|
|||
static inline T* _Nonnull untag_address(T* _Nonnull p) {
|
||||
return reinterpret_cast<T*>(untag_address(reinterpret_cast<uintptr_t>(p)));
|
||||
}
|
||||
|
||||
// MTE globals protects internal and external global variables. One of the main
|
||||
// things that MTE globals does is force all global variables accesses to go
|
||||
// through the GOT. In the linker though, some global variables are accessed (or
|
||||
// address-taken) prior to relocations being processed. Because relocations
|
||||
// haven't run yet, the GOT entry hasn't been populated, and this leads to
|
||||
// crashes. Thus, any globals used by the linker prior to relocation should be
|
||||
// annotated with this attribute, which suppresses tagging of this global
|
||||
// variable, restoring the pc-relative address computation.
|
||||
#if __has_feature(memtag_globals)
|
||||
#define BIONIC_USED_BEFORE_LINKER_RELOCATES __attribute__((no_sanitize("memtag")))
|
||||
#else // __has_feature(memtag_globals)
|
||||
#define BIONIC_USED_BEFORE_LINKER_RELOCATES
|
||||
#endif // __has_feature(memtag_globals)
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
|
@ -47,36 +46,6 @@ inline bool mte_supported() {
|
|||
return supported;
|
||||
}
|
||||
|
||||
inline void* get_tagged_address(const void* ptr) {
|
||||
#if defined(__aarch64__)
|
||||
if (mte_supported()) {
|
||||
__asm__ __volatile__(".arch_extension mte; ldg %0, [%0]" : "+r"(ptr));
|
||||
}
|
||||
#endif // aarch64
|
||||
return const_cast<void*>(ptr);
|
||||
}
|
||||
|
||||
// Inserts a random tag tag to `ptr`, using any of the set lower 16 bits in
|
||||
// `mask` to exclude the corresponding tag from being generated. Note: This does
|
||||
// not tag memory.
|
||||
inline void* insert_random_tag(const void* ptr, __attribute__((unused)) uint64_t mask = 0) {
|
||||
#if defined(__aarch64__)
|
||||
if (mte_supported() && ptr) {
|
||||
__asm__ __volatile__(".arch_extension mte; irg %0, %0, %1" : "+r"(ptr) : "r"(mask));
|
||||
}
|
||||
#endif // aarch64
|
||||
return const_cast<void*>(ptr);
|
||||
}
|
||||
|
||||
// Stores the address tag in `ptr` to memory, at `ptr`.
|
||||
inline void set_memory_tag(__attribute__((unused)) void* ptr) {
|
||||
#if defined(__aarch64__)
|
||||
if (mte_supported()) {
|
||||
__asm__ __volatile__(".arch_extension mte; stg %0, [%0]" : "+r"(ptr));
|
||||
}
|
||||
#endif // aarch64
|
||||
}
|
||||
|
||||
#ifdef __aarch64__
|
||||
class ScopedDisableMTE {
|
||||
size_t prev_tco_;
|
||||
|
|
|
@ -328,10 +328,6 @@ cc_defaults {
|
|||
|
||||
sanitize: {
|
||||
hwaddress: false,
|
||||
// TODO(mitchp): For now, disable MTE globals in the linker. MTE globals
|
||||
// change the relocation semantics, and GVs that are touched before the
|
||||
// linker has the chance to relocate itself have to be annotated.
|
||||
memtag_globals: false,
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
|
|
|
@ -331,7 +331,6 @@ soinfo* get_libdl_info(const soinfo& linker_si) {
|
|||
__libdl_info->gnu_bloom_filter_ = linker_si.gnu_bloom_filter_;
|
||||
__libdl_info->gnu_bucket_ = linker_si.gnu_bucket_;
|
||||
__libdl_info->gnu_chain_ = linker_si.gnu_chain_;
|
||||
__libdl_info->memtag_dynamic_entries_ = linker_si.memtag_dynamic_entries_;
|
||||
|
||||
__libdl_info->ref_count_ = 1;
|
||||
__libdl_info->strtab_size_ = linker_si.strtab_size_;
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
#include <android-base/scopeguard.h>
|
||||
#include <async_safe/log.h>
|
||||
#include <bionic/pthread_internal.h>
|
||||
#include <platform/bionic/mte.h>
|
||||
|
||||
// Private C library headers.
|
||||
|
||||
|
@ -2340,7 +2339,7 @@ bool do_dlsym(void* handle,
|
|||
void* tls_block = get_tls_block_for_this_thread(tls_module, /*should_alloc=*/true);
|
||||
*symbol = static_cast<char*>(tls_block) + sym->st_value;
|
||||
} else {
|
||||
*symbol = get_tagged_address(reinterpret_cast<void*>(found->resolve_symbol_address(sym)));
|
||||
*symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym));
|
||||
}
|
||||
failure_guard.Disable();
|
||||
LD_LOG(kLogDlsym,
|
||||
|
@ -2771,11 +2770,8 @@ bool soinfo::lookup_version_info(const VersionTracker& version_tracker, ElfW(Wor
|
|||
}
|
||||
|
||||
void soinfo::apply_relr_reloc(ElfW(Addr) offset) {
|
||||
ElfW(Addr)* tagged_address = reinterpret_cast<ElfW(Addr)*>(
|
||||
get_tagged_address(reinterpret_cast<void*>(offset + load_bias)));
|
||||
ElfW(Addr) tagged_result = reinterpret_cast<ElfW(Addr)>(
|
||||
get_tagged_address(reinterpret_cast<void*>(*tagged_address + load_bias)));
|
||||
*tagged_address = tagged_result;
|
||||
ElfW(Addr) address = offset + load_bias;
|
||||
*reinterpret_cast<ElfW(Addr)*>(address) += load_bias;
|
||||
}
|
||||
|
||||
// Process relocations in SHT_RELR section (experimental).
|
||||
|
@ -3308,18 +3304,6 @@ bool soinfo::prelink_image() {
|
|||
// it each time we look up a symbol with a version.
|
||||
if (!validate_verdef_section(this)) return false;
|
||||
|
||||
// MTE globals requires remapping data segments with PROT_MTE as anonymous mappings, because file
|
||||
// based mappings may not be backed by tag-capable memory (see "MAP_ANONYMOUS" on
|
||||
// https://www.kernel.org/doc/html/latest/arch/arm64/memory-tagging-extension.html). This is only
|
||||
// done if the binary has MTE globals (evidenced by the dynamic table entries), as it destroys
|
||||
// page sharing. It's also only done on devices that support MTE, because the act of remapping
|
||||
// pages is unnecessary on non-MTE devices (where we might still run MTE-globals enabled code).
|
||||
if (mte_supported() && memtag_globals() && memtag_globalssz() &&
|
||||
remap_memtag_globals_segments(phdr, phnum, base) == 0) {
|
||||
tag_globals();
|
||||
protect_memtag_globals_ro_segments(phdr, phnum, base);
|
||||
}
|
||||
|
||||
flags_ |= FLAG_PRELINKED;
|
||||
return true;
|
||||
}
|
||||
|
@ -3391,14 +3375,6 @@ bool soinfo::link_image(const SymbolLookupList& lookup_list, soinfo* local_group
|
|||
return false;
|
||||
}
|
||||
|
||||
if (mte_supported() && memtag_globals() && memtag_globalssz()) {
|
||||
// The linker's full path is not available until the main executable is loaded, as it's obtained
|
||||
// from DT_INTERP. We manually rename the linker's segments later, but have a best-effort name
|
||||
// in case we find a bug prior to loading the main executable.
|
||||
const char* soname = is_linker() ? "linker" : get_realpath();
|
||||
name_memtag_globals_segments(phdr, phnum, base, soname);
|
||||
}
|
||||
|
||||
/* Handle serializing/sharing the RELRO segment */
|
||||
if (extinfo && (extinfo->flags & ANDROID_DLEXT_WRITE_RELRO)) {
|
||||
if (phdr_table_serialize_gnu_relro(phdr, phnum, load_bias,
|
||||
|
@ -3431,47 +3407,6 @@ bool soinfo::protect_relro() {
|
|||
return true;
|
||||
}
|
||||
|
||||
// https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst#global-variable-tagging
|
||||
void soinfo::tag_globals() {
|
||||
if (is_linked()) return;
|
||||
if (flags_ & FLAG_GLOBALS_TAGGED) return;
|
||||
flags_ |= FLAG_GLOBALS_TAGGED;
|
||||
|
||||
constexpr size_t kTagGranuleSize = 16;
|
||||
const uint8_t* descriptor_stream = reinterpret_cast<const uint8_t*>(memtag_globals());
|
||||
|
||||
if (memtag_globalssz() == 0) {
|
||||
DL_ERR("Invalid memtag descriptor pool size: %zu", memtag_globalssz());
|
||||
}
|
||||
|
||||
uint64_t addr = 0;
|
||||
uleb128_decoder decoder(descriptor_stream, memtag_globalssz());
|
||||
// Don't ever generate tag zero, to easily distinguish between tagged and
|
||||
// untagged globals in register/tag dumps.
|
||||
uint64_t last_tag_mask = 1;
|
||||
constexpr uint64_t kMemtagStepVarintReservedBits = 3;
|
||||
|
||||
while (decoder.has_bytes()) {
|
||||
uint64_t value = decoder.pop_front();
|
||||
uint64_t step = value >> kMemtagStepVarintReservedBits;
|
||||
uint64_t granules_to_tag = value & ((1 << kMemtagStepVarintReservedBits) - 1);
|
||||
if (granules_to_tag == 0) {
|
||||
granules_to_tag = decoder.pop_front() + 1;
|
||||
}
|
||||
|
||||
addr += step * kTagGranuleSize;
|
||||
void* tagged_addr = insert_random_tag(reinterpret_cast<void*>(addr + load_bias), last_tag_mask);
|
||||
uint64_t tag = (reinterpret_cast<uint64_t>(tagged_addr) >> 56) & 0x0f;
|
||||
last_tag_mask = 1 | (1 << tag);
|
||||
|
||||
for (size_t k = 0; k < granules_to_tag; k++) {
|
||||
auto* granule = static_cast<uint8_t*>(tagged_addr) + k * kTagGranuleSize;
|
||||
set_memory_tag(static_cast<void*>(granule));
|
||||
}
|
||||
addr += granules_to_tag * kTagGranuleSize;
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan, bool is_hwasan) {
|
||||
g_default_namespace.set_isolated(false);
|
||||
auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : (
|
||||
|
|
|
@ -44,9 +44,7 @@
|
|||
#include "linker_tls.h"
|
||||
#include "linker_utils.h"
|
||||
|
||||
#include "platform/bionic/macros.h"
|
||||
#include "private/KernelArgumentBlock.h"
|
||||
#include "private/bionic_auxv.h"
|
||||
#include "private/bionic_call_ifunc_resolver.h"
|
||||
#include "private/bionic_globals.h"
|
||||
#include "private/bionic_tls.h"
|
||||
|
@ -80,8 +78,7 @@ void __libc_init_mte(const memtag_dynamic_entries_t* memtag_dynamic_entries, con
|
|||
// TODO (dimtiry): remove somain, rename solist to solist_head
|
||||
static soinfo* solist;
|
||||
static soinfo* sonext;
|
||||
// main process, always the one after libdl_info
|
||||
BIONIC_USED_BEFORE_LINKER_RELOCATES static soinfo* somain;
|
||||
static soinfo* somain; // main process, always the one after libdl_info
|
||||
static soinfo* solinker;
|
||||
static soinfo* vdso; // vdso if present
|
||||
|
||||
|
@ -393,16 +390,9 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load
|
|||
interp = kFallbackLinkerPath;
|
||||
}
|
||||
solinker->set_realpath(interp);
|
||||
if (solinker->memtag_globals() && solinker->memtag_globalssz()) {
|
||||
name_memtag_globals_segments(solinker->phdr, solinker->phnum, solinker->load_bias,
|
||||
solinker->get_realpath());
|
||||
}
|
||||
init_link_map_head(*solinker);
|
||||
|
||||
#if defined(__aarch64__)
|
||||
__libc_init_mte(somain->memtag_dynamic_entries(), somain->phdr, somain->phnum, somain->load_bias,
|
||||
args.argv);
|
||||
|
||||
if (exe_to_load == nullptr) {
|
||||
// Kernel does not add PROT_BTI to executable pages of the loaded ELF.
|
||||
// Apply appropriate protections here if it is needed.
|
||||
|
@ -414,6 +404,9 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load
|
|||
strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
__libc_init_mte(somain->memtag_dynamic_entries(), somain->phdr, somain->phnum, somain->load_bias,
|
||||
args.argv);
|
||||
#endif
|
||||
|
||||
// Register the main executable and the linker upfront to have
|
||||
|
@ -612,7 +605,7 @@ const unsigned kRelTag = DT_REL;
|
|||
const unsigned kRelSzTag = DT_RELSZ;
|
||||
#endif
|
||||
|
||||
BIONIC_USED_BEFORE_LINKER_RELOCATES extern __LIBC_HIDDEN__ ElfW(Ehdr) __ehdr_start;
|
||||
extern __LIBC_HIDDEN__ ElfW(Ehdr) __ehdr_start;
|
||||
|
||||
static void call_ifunc_resolvers_for_section(RelType* begin, RelType* end) {
|
||||
auto ehdr = reinterpret_cast<ElfW(Addr)>(&__ehdr_start);
|
||||
|
@ -670,16 +663,6 @@ static void linker_memclr(void* dst, size_t cnt) {
|
|||
}
|
||||
}
|
||||
|
||||
// Remapping MTE globals segments happens before the linker relocates itself, and so can't use
|
||||
// memcpy() from string.h. This function is compiled with -ffreestanding.
|
||||
void linker_memcpy(void* dest, const void* src, size_t n) {
|
||||
char* dest_bytes = reinterpret_cast<char*>(dest);
|
||||
const char* src_bytes = reinterpret_cast<const char*>(src);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
dest_bytes[i] = src_bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Detect an attempt to run the linker on itself. e.g.:
|
||||
// /system/bin/linker64 /system/bin/linker64
|
||||
// Use priority-1 to run this constructor before other constructors.
|
||||
|
|
|
@ -70,5 +70,3 @@ bool solist_remove_soinfo(soinfo* si);
|
|||
soinfo* solist_get_head();
|
||||
soinfo* solist_get_somain();
|
||||
soinfo* solist_get_vdso();
|
||||
|
||||
void linker_memcpy(void* dest, const void* src, size_t n);
|
|
@ -36,12 +36,10 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "android-base/unique_fd.h"
|
||||
#include "linker.h"
|
||||
#include "linker_debug.h"
|
||||
#include "linker_dlwarning.h"
|
||||
#include "linker_globals.h"
|
||||
#include "linker_main.h"
|
||||
#include "linker_debug.h"
|
||||
#include "linker_utils.h"
|
||||
|
||||
#include "private/CFIShadow.h" // For kLibraryAlignment
|
||||
|
@ -864,108 +862,6 @@ int phdr_table_protect_segments(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
|||
return _phdr_table_set_load_prot(phdr_table, phdr_count, load_bias, prot);
|
||||
}
|
||||
|
||||
static bool segment_needs_memtag_globals_remapping(const ElfW(Phdr) * phdr) {
|
||||
// For now, MTE globals is only supported on writeable data segments.
|
||||
return phdr->p_type == PT_LOAD && !(phdr->p_flags & PF_X) && (phdr->p_flags & PF_W);
|
||||
}
|
||||
|
||||
/* When MTE globals are requested by the binary, and when the hardware supports
|
||||
* it, remap the executable's PT_LOAD data pages to have PROT_MTE.
|
||||
*
|
||||
* Input:
|
||||
* phdr_table -> program header table
|
||||
* phdr_count -> number of entries in tables
|
||||
* load_bias -> load bias
|
||||
* Return:
|
||||
* 0 on success, -1 on failure (error code in errno).
|
||||
*/
|
||||
int remap_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias) {
|
||||
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
|
||||
if (!segment_needs_memtag_globals_remapping(phdr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
|
||||
uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
|
||||
size_t seg_page_aligned_size = seg_page_end - seg_page_start;
|
||||
|
||||
int prot = PFLAGS_TO_PROT(phdr->p_flags);
|
||||
// For anonymous private mappings, it may be possible to simply mprotect()
|
||||
// the PROT_MTE flag over the top. For file-based mappings, this will fail,
|
||||
// and we'll need to fall back. We also allow PROT_WRITE here to allow
|
||||
// writing memory tags (in `soinfo::tag_globals()`), and set these sections
|
||||
// back to read-only after tags are applied (similar to RELRO).
|
||||
#if defined(__aarch64__)
|
||||
prot |= PROT_MTE;
|
||||
#endif // defined(__aarch64__)
|
||||
if (mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
|
||||
prot | PROT_WRITE) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
void* mapping_copy = mmap(nullptr, seg_page_aligned_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
linker_memcpy(mapping_copy, reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size);
|
||||
|
||||
void* seg_addr = mmap(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size,
|
||||
prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (seg_addr == MAP_FAILED) return -1;
|
||||
|
||||
linker_memcpy(seg_addr, mapping_copy, seg_page_aligned_size);
|
||||
munmap(mapping_copy, seg_page_aligned_size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void protect_memtag_globals_ro_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias) {
|
||||
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
|
||||
int prot = PFLAGS_TO_PROT(phdr->p_flags);
|
||||
if (!segment_needs_memtag_globals_remapping(phdr) || (prot & PROT_WRITE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(__aarch64__)
|
||||
prot |= PROT_MTE;
|
||||
#endif // defined(__aarch64__)
|
||||
|
||||
uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
|
||||
uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
|
||||
size_t seg_page_aligned_size = seg_page_end - seg_page_start;
|
||||
mprotect(reinterpret_cast<void*>(seg_page_start), seg_page_aligned_size, prot);
|
||||
}
|
||||
}
|
||||
|
||||
void name_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias, const char* soname) {
|
||||
for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_table + phdr_count; phdr++) {
|
||||
if (!segment_needs_memtag_globals_remapping(phdr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uintptr_t seg_page_start = page_start(phdr->p_vaddr) + load_bias;
|
||||
uintptr_t seg_page_end = page_end(phdr->p_vaddr + phdr->p_memsz) + load_bias;
|
||||
size_t seg_page_aligned_size = seg_page_end - seg_page_start;
|
||||
|
||||
// For file-based mappings that we're now forcing to be anonymous mappings, set the VMA name to
|
||||
// make debugging easier. The previous Android-kernel specific implementation captured the name
|
||||
// by pointer from userspace, which meant we had to persist the name permanently in memory.
|
||||
// Since Android13-5.10 (https://android-review.git.corp.google.com/c/kernel/common/+/1934723)
|
||||
// though, we use the upstream-kernel implementation
|
||||
// (https://github.com/torvalds/linux/commit/9a10064f5625d5572c3626c1516e0bebc6c9fe9b), which
|
||||
// copies the name into kernel memory. It's a safe bet that any devices with Android 14 are
|
||||
// using a kernel >= 5.10.
|
||||
constexpr unsigned kVmaNameLimit = 80;
|
||||
char vma_name[kVmaNameLimit];
|
||||
async_safe_format_buffer(vma_name, kVmaNameLimit, "memtag:%s+0x%" PRIxPTR, soname,
|
||||
page_start(phdr->p_vaddr));
|
||||
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, reinterpret_cast<void*>(seg_page_start),
|
||||
seg_page_aligned_size, vma_name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Change the protection of all loaded segments in memory to writable.
|
||||
* This is useful before performing relocations. Once completed, you
|
||||
* will have to call phdr_table_protect_segments to restore the original
|
||||
|
|
|
@ -148,12 +148,3 @@ void phdr_table_get_dynamic_section(const ElfW(Phdr)* phdr_table, size_t phdr_co
|
|||
|
||||
const char* phdr_table_get_interpreter_name(const ElfW(Phdr)* phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias);
|
||||
|
||||
int remap_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias);
|
||||
|
||||
void protect_memtag_globals_ro_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias);
|
||||
|
||||
void name_memtag_globals_segments(const ElfW(Phdr) * phdr_table, size_t phdr_count,
|
||||
ElfW(Addr) load_bias, const char* soname);
|
||||
|
|
|
@ -44,8 +44,6 @@
|
|||
#include "linker_soinfo.h"
|
||||
#include "private/bionic_globals.h"
|
||||
|
||||
#include <platform/bionic/mte.h>
|
||||
|
||||
static bool is_tls_reloc(ElfW(Word) type) {
|
||||
switch (type) {
|
||||
case R_GENERIC_TLS_DTPMOD:
|
||||
|
@ -159,19 +157,12 @@ void print_linker_stats() {
|
|||
|
||||
static bool process_relocation_general(Relocator& relocator, const rel_t& reloc);
|
||||
|
||||
static ElfW(Addr) apply_memtag(ElfW(Addr) sym_addr) {
|
||||
if (sym_addr == 0) return sym_addr; // Handle undefined weak symbols.
|
||||
|
||||
return reinterpret_cast<ElfW(Addr)>(get_tagged_address(reinterpret_cast<void*>(sym_addr)));
|
||||
}
|
||||
|
||||
template <RelocMode Mode>
|
||||
__attribute__((always_inline))
|
||||
static bool process_relocation_impl(Relocator& relocator, const rel_t& reloc) {
|
||||
constexpr bool IsGeneral = Mode == RelocMode::General;
|
||||
|
||||
void* const rel_target =
|
||||
reinterpret_cast<void*>(apply_memtag(reloc.r_offset + relocator.si->load_bias));
|
||||
void* const rel_target = reinterpret_cast<void*>(reloc.r_offset + relocator.si->load_bias);
|
||||
const uint32_t r_type = ELFW(R_TYPE)(reloc.r_info);
|
||||
const uint32_t r_sym = ELFW(R_SYM)(reloc.r_info);
|
||||
|
||||
|
@ -334,7 +325,7 @@ static bool process_relocation_impl(Relocator& relocator, const rel_t& reloc) {
|
|||
// common in non-platform binaries.
|
||||
if (r_type == R_GENERIC_ABSOLUTE) {
|
||||
count_relocation_if<IsGeneral>(kRelocAbsolute);
|
||||
const ElfW(Addr) result = apply_memtag(sym_addr) + get_addend_rel();
|
||||
const ElfW(Addr) result = sym_addr + get_addend_rel();
|
||||
trace_reloc("RELO ABSOLUTE %16p <- %16p %s",
|
||||
rel_target, reinterpret_cast<void*>(result), sym_name);
|
||||
*static_cast<ElfW(Addr)*>(rel_target) = result;
|
||||
|
@ -344,29 +335,16 @@ static bool process_relocation_impl(Relocator& relocator, const rel_t& reloc) {
|
|||
// document (IHI0044F) specifies that R_ARM_GLOB_DAT has an addend, but Bionic isn't adding
|
||||
// it.
|
||||
count_relocation_if<IsGeneral>(kRelocAbsolute);
|
||||
ElfW(Addr) result = apply_memtag(sym_addr) + get_addend_norel();
|
||||
trace_reloc("RELO GLOB_DAT %16p <- %16p %s", rel_target, reinterpret_cast<void*>(result),
|
||||
sym_name);
|
||||
const ElfW(Addr) result = sym_addr + get_addend_norel();
|
||||
trace_reloc("RELO GLOB_DAT %16p <- %16p %s",
|
||||
rel_target, reinterpret_cast<void*>(result), sym_name);
|
||||
*static_cast<ElfW(Addr)*>(rel_target) = result;
|
||||
return true;
|
||||
} else if (r_type == R_GENERIC_RELATIVE) {
|
||||
// In practice, r_sym is always zero, but if it weren't, the linker would still look up the
|
||||
// referenced symbol (and abort if the symbol isn't found), even though it isn't used.
|
||||
count_relocation_if<IsGeneral>(kRelocRelative);
|
||||
ElfW(Addr) result;
|
||||
// MTE globals reuses the place bits for additional tag-derivation metadata for
|
||||
// R_AARCH64_RELATIVE relocations, which makes it incompatible with
|
||||
// `-Wl,--apply-dynamic-relocs`. This is enforced by lld, however there's nothing stopping
|
||||
// Android binaries (particularly prebuilts) from building with this linker flag if they're
|
||||
// not built with MTE globals. Thus, don't use the new relocation semantics if this DSO
|
||||
// doesn't have MTE globals.
|
||||
if (relocator.si->memtag_globals() && relocator.si->memtag_globalssz()) {
|
||||
int64_t* place = static_cast<int64_t*>(rel_target);
|
||||
int64_t offset = *place;
|
||||
result = apply_memtag(relocator.si->load_bias + get_addend_rel() + offset) - offset;
|
||||
} else {
|
||||
result = relocator.si->load_bias + get_addend_rel();
|
||||
}
|
||||
const ElfW(Addr) result = relocator.si->load_bias + get_addend_rel();
|
||||
trace_reloc("RELO RELATIVE %16p <- %16p",
|
||||
rel_target, reinterpret_cast<void*>(result));
|
||||
*static_cast<ElfW(Addr)*>(rel_target) = result;
|
||||
|
|
|
@ -69,32 +69,3 @@ class sleb128_decoder {
|
|||
const uint8_t* current_;
|
||||
const uint8_t* const end_;
|
||||
};
|
||||
|
||||
class uleb128_decoder {
|
||||
public:
|
||||
uleb128_decoder(const uint8_t* buffer, size_t count) : current_(buffer), end_(buffer + count) {}
|
||||
|
||||
uint64_t pop_front() {
|
||||
uint64_t value = 0;
|
||||
|
||||
size_t shift = 0;
|
||||
uint8_t byte;
|
||||
|
||||
do {
|
||||
if (current_ >= end_) {
|
||||
async_safe_fatal("uleb128_decoder ran out of bounds");
|
||||
}
|
||||
byte = *current_++;
|
||||
value |= (static_cast<size_t>(byte & 127) << shift);
|
||||
shift += 7;
|
||||
} while (byte & 128);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool has_bytes() { return current_ < end_; }
|
||||
|
||||
private:
|
||||
const uint8_t* current_;
|
||||
const uint8_t* const end_;
|
||||
};
|
||||
|
|
|
@ -66,10 +66,9 @@
|
|||
// soinfo is executed and this flag is
|
||||
// unset.
|
||||
#define FLAG_PRELINKED 0x00000400 // prelink_image has successfully processed this soinfo
|
||||
#define FLAG_GLOBALS_TAGGED 0x00000800 // globals have been tagged by MTE.
|
||||
#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format
|
||||
|
||||
#define SOINFO_VERSION 7
|
||||
#define SOINFO_VERSION 6
|
||||
|
||||
ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr);
|
||||
|
||||
|
@ -258,8 +257,6 @@ struct soinfo {
|
|||
const android_dlextinfo* extinfo, size_t* relro_fd_offset);
|
||||
bool protect_relro();
|
||||
|
||||
void tag_globals();
|
||||
|
||||
void add_child(soinfo* child);
|
||||
void remove_all_links();
|
||||
|
||||
|
|
|
@ -353,89 +353,6 @@ cc_prebuilt_test_library_shared {
|
|||
},
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "memtag_globals_defaults",
|
||||
defaults: [
|
||||
"bionic_testlib_defaults",
|
||||
"bionic_targets_only"
|
||||
],
|
||||
cflags: [
|
||||
"-Wno-array-bounds",
|
||||
"-Wno-unused-variable",
|
||||
],
|
||||
header_libs: ["bionic_libc_platform_headers"],
|
||||
sanitize: {
|
||||
hwaddress: false,
|
||||
memtag_heap: true,
|
||||
memtag_globals: true,
|
||||
diag: {
|
||||
memtag_heap: true,
|
||||
}
|
||||
},
|
||||
relative_install_path: "bionic-loader-test-libs/prebuilt-elf-files",
|
||||
}
|
||||
|
||||
/// The following library and binary is to be used for testing the bionic linker
|
||||
/// against memtag globals. While we wait for the compiler to be updated, the
|
||||
/// files come as prebuilts (built using) the corresponding 'regular'
|
||||
/// cc_test_library and cc_test rules below, just with a tip-of-tree version of
|
||||
/// LLVM.
|
||||
///
|
||||
/// Updating the prebuilts can be done by installing a tip-of-tree compiler, and
|
||||
/// running:
|
||||
/// - m memtag_globals_binary memtag_globals_dso
|
||||
/// - cp $ANDROID_PRODUCT_OUT/symbols/data/nativetest64/bionic-loader-test-libs/prebuilt-elf-files/memtag_globals_* \
|
||||
/// $ANDROID_BUILD_TOP/bionic/tests/prebuilt-elf-files/arm64/
|
||||
///
|
||||
// cc_test_library {
|
||||
// name: "memtag_globals_dso",
|
||||
// defaults: [ "memtag_globals_defaults" ],
|
||||
// srcs: ["prebuilt-elf-files/arm64/src/memtag_globals_dso.cpp"],
|
||||
// }
|
||||
//
|
||||
// cc_test {
|
||||
// name: "memtag_globals_binary",
|
||||
// // Not actually a '.so' file, this is an executable; but the only way to
|
||||
// // get soong to be able to produce a properly located and linked prebuilt
|
||||
// // variant of this binary was through the cc_prebuilt_test_library_shared
|
||||
// // rule, which appends '.so' to the end. Once the prebuilts are removed,
|
||||
// // the suffix can be removed.
|
||||
// suffix: ".so",
|
||||
// defaults: [ "memtag_globals_defaults" ],
|
||||
// srcs: ["prebuilt-elf-files/arm64/src/memtag_globals_binary.cpp"],
|
||||
// shared_libs: [ "memtag_globals_dso" ],
|
||||
// // This binary is used in the bionic-unit-tests as a data dependency, and is
|
||||
// // in the same folder as memtag_globals_dso. But, the default cc_test rules
|
||||
// // make this binary (when just explicitly built and shoved in
|
||||
// // /data/nativetest64/) end up in a subfolder called
|
||||
// // 'memtag_globals_binary'. When this happens, the explicit build fails to
|
||||
// // find the DSO because the default rpath is just ${ORIGIN}, and because we
|
||||
// // want this to be usable both from bionic-unit-tests and explicit builds,
|
||||
// // let's just not put it in a subdirectory.
|
||||
// no_named_install_directory: true,
|
||||
// }
|
||||
|
||||
cc_prebuilt_test_library_shared {
|
||||
name: "memtag_globals_dso",
|
||||
defaults: [ "memtag_globals_defaults" ],
|
||||
arch: {
|
||||
arm64: {
|
||||
srcs: ["prebuilt-elf-files/arm64/memtag_globals_dso.so"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
cc_prebuilt_test_library_shared {
|
||||
name: "memtag_globals_binary",
|
||||
defaults: [ "memtag_globals_defaults" ],
|
||||
arch: {
|
||||
arm64: {
|
||||
srcs: ["prebuilt-elf-files/arm64/memtag_globals_binary.so"],
|
||||
shared_libs: [ "memtag_globals_dso" ],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// All standard tests.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -510,7 +427,6 @@ cc_test_library {
|
|||
"math_test.cpp",
|
||||
"membarrier_test.cpp",
|
||||
"memtag_stack_test.cpp",
|
||||
"memtag_globals_test.cpp",
|
||||
"mntent_test.cpp",
|
||||
"mte_test.cpp",
|
||||
"netdb_test.cpp",
|
||||
|
@ -966,8 +882,6 @@ cc_defaults {
|
|||
"preinit_syscall_test_helper",
|
||||
"thread_exit_cb_helper",
|
||||
"tls_properties_helper",
|
||||
"memtag_globals_binary",
|
||||
"memtag_globals_dso",
|
||||
],
|
||||
data_libs: [
|
||||
"libatest_simple_zip",
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
#include "gtest_globals.h"
|
||||
#include "platform/bionic/mte.h"
|
||||
#include "utils.h"
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
|
||||
TEST(MemtagGlobalsTest, test) {
|
||||
#if defined(__BIONIC__) && defined(__aarch64__)
|
||||
std::string binary = GetPrebuiltElfDir() + "/memtag_globals_binary.so";
|
||||
chmod(binary.c_str(), 0755);
|
||||
ExecTestHelper eth;
|
||||
eth.SetArgs({binary.c_str(), nullptr});
|
||||
eth.Run(
|
||||
[&]() {
|
||||
execve(binary.c_str(), eth.GetArgs(), eth.GetEnv());
|
||||
GTEST_FAIL() << "Failed to execve: " << strerror(errno) << "\n";
|
||||
},
|
||||
// We catch the global-buffer-overflow and crash only when MTE is
|
||||
// supported.
|
||||
mte_supported() ? -SIGSEGV : 0, "Assertions were passed");
|
||||
#else
|
||||
GTEST_SKIP() << "bionic/arm64 only";
|
||||
#endif
|
||||
}
|
Binary file not shown.
Binary file not shown.
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
void check_tagged(const void* a);
|
||||
void check_untagged(const void* a);
|
||||
void check_matching_tags(const void* a, const void* b);
|
||||
void check_eq(const void* a, const void* b);
|
||||
|
||||
void dso_check_assertions(bool enforce_tagged);
|
||||
void dso_print_variables();
|
||||
|
||||
void print_variable_address(const char* name, const void* ptr);
|
||||
void print_variables(const char* header,
|
||||
const std::vector<std::pair<const char*, const void*>>& tagged_variables,
|
||||
const std::vector<std::pair<const char*, const void*>>& untagged_variables);
|
|
@ -1,195 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "memtag_globals.h"
|
||||
|
||||
// Adapted from the LLD test suite: lld/test/ELF/Inputs/aarch64-memtag-globals.s
|
||||
|
||||
/// Global variables defined here, of various semantics.
|
||||
char global[30] = {};
|
||||
__attribute__((no_sanitize("memtag"))) int global_untagged = 0;
|
||||
const int const_global = 0;
|
||||
static const int hidden_const_global = 0;
|
||||
static char hidden_global[12] = {};
|
||||
__attribute__((visibility("hidden"))) int hidden_attr_global = 0;
|
||||
__attribute__((visibility("hidden"))) const int hidden_attr_const_global = 0;
|
||||
|
||||
/// Should be untagged.
|
||||
__thread int tls_global;
|
||||
__thread static int hidden_tls_global;
|
||||
|
||||
/// Tagged, from the other file.
|
||||
extern int global_extern;
|
||||
/// Untagged, from the other file.
|
||||
extern __attribute__((no_sanitize("memtag"))) int global_extern_untagged;
|
||||
/// Tagged here, but untagged in the definition found in the sister objfile
|
||||
/// (explicitly).
|
||||
extern int global_extern_untagged_definition_but_tagged_import;
|
||||
|
||||
/// ABS64 relocations. Also, forces symtab entries for local and external
|
||||
/// globals.
|
||||
char* pointer_to_global = &global[0];
|
||||
char* pointer_inside_global = &global[17];
|
||||
char* pointer_to_global_end = &global[30];
|
||||
char* pointer_past_global_end = &global[48];
|
||||
int* pointer_to_global_untagged = &global_untagged;
|
||||
const int* pointer_to_const_global = &const_global;
|
||||
/// RELATIVE relocations.
|
||||
const int* pointer_to_hidden_const_global = &hidden_const_global;
|
||||
char* pointer_to_hidden_global = &hidden_global[0];
|
||||
int* pointer_to_hidden_attr_global = &hidden_attr_global;
|
||||
const int* pointer_to_hidden_attr_const_global = &hidden_attr_const_global;
|
||||
/// RELATIVE relocations with special AArch64 MemtagABI semantics, with the
|
||||
/// offset ('12' or '16') encoded in the place.
|
||||
char* pointer_to_hidden_global_end = &hidden_global[12];
|
||||
char* pointer_past_hidden_global_end = &hidden_global[16];
|
||||
/// ABS64 relocations.
|
||||
int* pointer_to_global_extern = &global_extern;
|
||||
int* pointer_to_global_extern_untagged = &global_extern_untagged;
|
||||
int* pointer_to_global_extern_untagged_definition_but_tagged_import =
|
||||
&global_extern_untagged_definition_but_tagged_import;
|
||||
|
||||
// Force materialization of these globals into the symtab.
|
||||
int* get_address_to_tls_global() {
|
||||
return &tls_global;
|
||||
}
|
||||
int* get_address_to_hidden_tls_global() {
|
||||
return &hidden_tls_global;
|
||||
}
|
||||
|
||||
static const std::vector<std::pair<const char*, const void*>>& get_expected_tagged_vars() {
|
||||
static std::vector<std::pair<const char*, const void*>> expected_tagged_vars = {
|
||||
{"global", &global},
|
||||
{"pointer_inside_global", pointer_inside_global},
|
||||
{"pointer_to_global_end", pointer_to_global_end},
|
||||
{"pointer_past_global_end", pointer_past_global_end},
|
||||
{"hidden_global", &hidden_global},
|
||||
{"hidden_attr_global", &hidden_attr_global},
|
||||
{"global_extern", &global_extern},
|
||||
};
|
||||
return expected_tagged_vars;
|
||||
}
|
||||
|
||||
static const std::vector<std::pair<const char*, const void*>>& get_expected_untagged_vars() {
|
||||
static std::vector<std::pair<const char*, const void*>> expected_untagged_vars = {
|
||||
{"global_extern_untagged", &global_extern_untagged},
|
||||
{"global_extern_untagged_definition_but_tagged_import",
|
||||
&global_extern_untagged_definition_but_tagged_import},
|
||||
{"global_untagged", &global_untagged},
|
||||
{"const_global", &const_global},
|
||||
{"hidden_const_global", &hidden_const_global},
|
||||
{"hidden_attr_const_global", &hidden_attr_const_global},
|
||||
{"tls_global", &tls_global},
|
||||
{"hidden_tls_global", &hidden_tls_global},
|
||||
};
|
||||
return expected_untagged_vars;
|
||||
}
|
||||
|
||||
void exe_print_variables() {
|
||||
print_variables(" Variables accessible from the binary:\n", get_expected_tagged_vars(),
|
||||
get_expected_untagged_vars());
|
||||
}
|
||||
|
||||
// Dump the addresses of the global variables to stderr
|
||||
void dso_print();
|
||||
void dso_print_others();
|
||||
|
||||
void exe_check_assertions(bool check_pointers_are_tagged) {
|
||||
// Check that non-const variables are writeable.
|
||||
*pointer_to_global = 0;
|
||||
*pointer_inside_global = 0;
|
||||
*(pointer_to_global_end - 1) = 0;
|
||||
*pointer_to_global_untagged = 0;
|
||||
*pointer_to_hidden_global = 0;
|
||||
*pointer_to_hidden_attr_global = 0;
|
||||
*(pointer_to_hidden_global_end - 1) = 0;
|
||||
*pointer_to_global_extern = 0;
|
||||
*pointer_to_global_extern_untagged = 0;
|
||||
*pointer_to_global_extern_untagged_definition_but_tagged_import = 0;
|
||||
|
||||
if (check_pointers_are_tagged) {
|
||||
for (const auto& [_, pointer] : get_expected_tagged_vars()) {
|
||||
check_tagged(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [_, pointer] : get_expected_untagged_vars()) {
|
||||
check_untagged(pointer);
|
||||
}
|
||||
|
||||
check_matching_tags(pointer_to_global, pointer_inside_global);
|
||||
check_matching_tags(pointer_to_global, pointer_to_global_end);
|
||||
check_matching_tags(pointer_to_global, pointer_past_global_end);
|
||||
check_eq(pointer_inside_global, pointer_to_global + 17);
|
||||
check_eq(pointer_to_global_end, pointer_to_global + 30);
|
||||
check_eq(pointer_past_global_end, pointer_to_global + 48);
|
||||
|
||||
check_matching_tags(pointer_to_hidden_global, pointer_to_hidden_global_end);
|
||||
check_matching_tags(pointer_to_hidden_global, pointer_past_hidden_global_end);
|
||||
check_eq(pointer_to_hidden_global_end, pointer_to_hidden_global + 12);
|
||||
check_eq(pointer_past_hidden_global_end, pointer_to_hidden_global + 16);
|
||||
}
|
||||
|
||||
void crash() {
|
||||
*pointer_past_global_end = 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
bool check_pointers_are_tagged = false;
|
||||
// For an MTE-capable device, provide argv[1] == '1' to enable the assertions
|
||||
// that pointers should be tagged.
|
||||
if (argc >= 2 && argv[1][0] == '1') {
|
||||
check_pointers_are_tagged = true;
|
||||
}
|
||||
|
||||
char* heap_ptr = static_cast<char*>(malloc(1));
|
||||
print_variable_address("heap address", heap_ptr);
|
||||
*heap_ptr = 0;
|
||||
if (check_pointers_are_tagged) check_tagged(heap_ptr);
|
||||
free(heap_ptr);
|
||||
|
||||
exe_print_variables();
|
||||
dso_print_variables();
|
||||
|
||||
exe_check_assertions(check_pointers_are_tagged);
|
||||
dso_check_assertions(check_pointers_are_tagged);
|
||||
|
||||
printf("Assertions were passed. Now doing a global-buffer-overflow.\n");
|
||||
fflush(stdout);
|
||||
crash();
|
||||
printf("global-buffer-overflow went uncaught.\n");
|
||||
return 0;
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
#include "memtag_globals.h"
|
||||
|
||||
// Adapted from the LLD test suite: lld/test/ELF/Inputs/aarch64-memtag-globals.s
|
||||
|
||||
int global_extern;
|
||||
static int global_extern_hidden;
|
||||
__attribute__((no_sanitize("memtag"))) int global_extern_untagged;
|
||||
__attribute__((no_sanitize("memtag"))) int global_extern_untagged_definition_but_tagged_import;
|
||||
|
||||
void assertion_failure() {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void check_tagged(const void* a) {
|
||||
uintptr_t a_uptr = reinterpret_cast<uintptr_t>(a);
|
||||
#if defined(__aarch64__)
|
||||
if ((a_uptr >> 56) == 0) {
|
||||
fprintf(stderr, "**********************************\n");
|
||||
fprintf(stderr, "Failed assertion:\n");
|
||||
fprintf(stderr, " tag(0x%zx) != 0\n", a_uptr);
|
||||
fprintf(stderr, "**********************************\n");
|
||||
|
||||
assertion_failure();
|
||||
}
|
||||
#endif // defined(__aarch64__)
|
||||
}
|
||||
|
||||
void check_untagged(const void* a) {
|
||||
uintptr_t a_uptr = reinterpret_cast<uintptr_t>(a);
|
||||
#if defined(__aarch64__)
|
||||
if ((a_uptr >> 56) != 0) {
|
||||
fprintf(stderr, "**********************************\n");
|
||||
fprintf(stderr, "Failed assertion:\n");
|
||||
fprintf(stderr, " tag(0x%zx) == 0\n", a_uptr);
|
||||
fprintf(stderr, "**********************************\n");
|
||||
|
||||
assertion_failure();
|
||||
}
|
||||
#endif // defined(__aarch64__)
|
||||
}
|
||||
|
||||
void check_matching_tags(const void* a, const void* b) {
|
||||
uintptr_t a_uptr = reinterpret_cast<uintptr_t>(a);
|
||||
uintptr_t b_uptr = reinterpret_cast<uintptr_t>(b);
|
||||
#if defined(__aarch64__)
|
||||
if (a_uptr >> 56 != b_uptr >> 56) {
|
||||
fprintf(stderr, "**********************************\n");
|
||||
fprintf(stderr, "Failed assertion:\n");
|
||||
fprintf(stderr, " tag(0x%zx) != tag(0x%zx)\n", a_uptr, b_uptr);
|
||||
fprintf(stderr, "**********************************\n");
|
||||
|
||||
assertion_failure();
|
||||
}
|
||||
#endif // defined(__aarch64__)
|
||||
}
|
||||
|
||||
void check_eq(const void* a, const void* b) {
|
||||
if (a != b) {
|
||||
fprintf(stderr, "**********************************\n");
|
||||
fprintf(stderr, "Failed assertion:\n");
|
||||
fprintf(stderr, " %p != %p\n", a, b);
|
||||
fprintf(stderr, "**********************************\n");
|
||||
|
||||
assertion_failure();
|
||||
}
|
||||
}
|
||||
|
||||
#define LONGEST_VARIABLE_NAME "51"
|
||||
void print_variable_address(const char* name, const void* ptr) {
|
||||
printf("%" LONGEST_VARIABLE_NAME "s: %16p\n", name, ptr);
|
||||
}
|
||||
|
||||
static const std::vector<std::pair<const char*, const void*>>& get_expected_tagged_vars() {
|
||||
static std::vector<std::pair<const char*, const void*>> expected_tagged_vars = {
|
||||
{"global_extern", &global_extern},
|
||||
{"global_extern_hidden", &global_extern_hidden},
|
||||
};
|
||||
return expected_tagged_vars;
|
||||
}
|
||||
|
||||
static const std::vector<std::pair<const char*, const void*>>& get_expected_untagged_vars() {
|
||||
static std::vector<std::pair<const char*, const void*>> expected_untagged_vars = {
|
||||
{"global_extern_untagged", &global_extern_untagged},
|
||||
{"global_extern_untagged_definition_but_tagged_import",
|
||||
&global_extern_untagged_definition_but_tagged_import},
|
||||
};
|
||||
return expected_untagged_vars;
|
||||
}
|
||||
|
||||
void dso_print_variables() {
|
||||
print_variables(" Variables declared in the DSO:\n", get_expected_tagged_vars(),
|
||||
get_expected_untagged_vars());
|
||||
}
|
||||
|
||||
void print_variables(const char* header,
|
||||
const std::vector<std::pair<const char*, const void*>>& tagged_variables,
|
||||
const std::vector<std::pair<const char*, const void*>>& untagged_variables) {
|
||||
printf("==========================================================\n");
|
||||
printf("%s", header);
|
||||
printf("==========================================================\n");
|
||||
printf(" Variables expected to be tagged:\n");
|
||||
printf("----------------------------------------------------------\n");
|
||||
for (const auto& [name, pointer] : tagged_variables) {
|
||||
print_variable_address(name, pointer);
|
||||
}
|
||||
|
||||
printf("\n----------------------------------------------------------\n");
|
||||
printf(" Variables expected to be untagged:\n");
|
||||
printf("----------------------------------------------------------\n");
|
||||
for (const auto& [name, pointer] : untagged_variables) {
|
||||
print_variable_address(name, pointer);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void dso_check_assertions(bool check_pointers_are_tagged) {
|
||||
// Check that non-const variables are writeable.
|
||||
global_extern = 0;
|
||||
global_extern_hidden = 0;
|
||||
global_extern_untagged = 0;
|
||||
global_extern_untagged_definition_but_tagged_import = 0;
|
||||
|
||||
if (check_pointers_are_tagged) {
|
||||
for (const auto& [_, pointer] : get_expected_tagged_vars()) {
|
||||
check_tagged(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [_, pointer] : get_expected_untagged_vars()) {
|
||||
check_untagged(pointer);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue