Use ELF notes to set the desired memory tagging level.

Use a note in executables to specify
(none|sync|async) heap tagging level. To be extended with (heap x stack x
globals) in the future. A missing note disables all tagging.

Bug: b/135772972
Test: bionic-unit-tests (in a future change)

Change-Id: Iab145a922c7abe24cdce17323f9e0c1063cc1321
This commit is contained in:
Evgenii Stepanov 2020-12-15 13:55:32 -08:00
parent dec48bdd9e
commit 8564b8d9e6
13 changed files with 279 additions and 28 deletions

View file

@ -2145,6 +2145,28 @@ cc_object {
defaults: ["crt_defaults"],
}
cc_library_static {
name: "note_memtag_heap_async",
arch: {
arm64: {
srcs: ["arch-arm64/bionic/note_memtag_heap_async.S"],
}
},
defaults: ["crt_defaults"],
}
cc_library_static {
name: "note_memtag_heap_sync",
arch: {
arm64: {
srcs: ["arch-arm64/bionic/note_memtag_heap_sync.S"],
}
},
defaults: ["crt_defaults"],
}
// ========================================================
// NDK headers.
// ========================================================

View file

@ -1083,6 +1083,34 @@ SUCH DAMAGE.
-------------------------------------------------------------------
Copyright (C) 2021 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.
-------------------------------------------------------------------
Copyright (c) 1980, 1983, 1988, 1993
The Regents of the University of California. All rights reserved.

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2021 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 <private/bionic_asm.h>
#include <private/bionic_asm_note.h>
__bionic_asm_custom_note_gnu_section()
.section ".note.android.memtag", "a", %note
.p2align 2
.long 1f - 0f // int32_t namesz
.long 3f - 2f // int32_t descsz
.long NT_TYPE_MEMTAG // int32_t type
0:
.asciz "Android" // char name[]
1:
.p2align 2
2:
.long (NT_MEMTAG_LEVEL_ASYNC | NT_MEMTAG_HEAP) // value
3:
.p2align 2

View file

@ -0,0 +1,46 @@
/*
* Copyright (C) 2021 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 <private/bionic_asm.h>
#include <private/bionic_asm_note.h>
__bionic_asm_custom_note_gnu_section()
.section ".note.android.memtag", "a", %note
.p2align 2
.long 1f - 0f // int32_t namesz
.long 3f - 2f // int32_t descsz
.long NT_TYPE_MEMTAG // int32_t type
0:
.asciz "Android" // char name[]
1:
.p2align 2
2:
.long (NT_MEMTAG_LEVEL_SYNC | NT_MEMTAG_HEAP) // value
3:
.p2align 2

View file

@ -42,30 +42,29 @@ static HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
void SetDefaultHeapTaggingLevel() {
#if defined(__aarch64__)
#ifdef ANDROID_EXPERIMENTAL_MTE
// First, try enabling MTE in asynchronous mode, with tag 0 excluded. This will fail if the kernel
// or hardware doesn't support MTE, and we will fall back to just enabling tagged pointers in
// syscall arguments.
if (prctl(PR_SET_TAGGED_ADDR_CTRL,
PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT), 0, 0,
0) == 0) {
heap_tagging_level = M_HEAP_TAGGING_LEVEL_ASYNC;
return;
}
#endif // ANDROID_EXPERIMENTAL_MTE
// Allow the kernel to accept tagged pointers in syscall arguments. This is a no-op (kernel
// returns -EINVAL) if the kernel doesn't understand the prctl.
if (prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == 0) {
#if !__has_feature(hwaddress_sanitizer)
heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
__libc_globals.mutate([](libc_globals* globals) {
// Arrange for us to set pointer tags to POINTER_TAG, check tags on
// deallocation and untag when passing pointers to the allocator.
globals->heap_pointer_tag = (reinterpret_cast<uintptr_t>(POINTER_TAG) << TAG_SHIFT) |
(0xffull << CHECK_SHIFT) | (0xffull << UNTAG_SHIFT);
});
#endif // hwaddress_sanitizer
heap_tagging_level = __libc_shared_globals()->initial_heap_tagging_level;
#endif
switch (heap_tagging_level) {
case M_HEAP_TAGGING_LEVEL_TBI:
__libc_globals.mutate([](libc_globals* globals) {
// Arrange for us to set pointer tags to POINTER_TAG, check tags on
// deallocation and untag when passing pointers to the allocator.
globals->heap_pointer_tag = (reinterpret_cast<uintptr_t>(POINTER_TAG) << TAG_SHIFT) |
(0xffull << CHECK_SHIFT) | (0xffull << UNTAG_SHIFT);
});
break;
#if defined(ANDROID_EXPERIMENTAL_MTE) && defined(USE_SCUDO)
case M_HEAP_TAGGING_LEVEL_SYNC:
scudo_malloc_set_track_allocation_stacks(1);
break;
case M_HEAP_TAGGING_LEVEL_NONE:
scudo_malloc_disable_memory_tagging();
break;
#endif // ANDROID_EXPERIMENTAL_MTE
default:
break;
}
#endif // aarch64
}

View file

@ -86,7 +86,7 @@ static void arc4random_fork_handler() {
_thread_arc4_lock();
}
static void __libc_init_malloc_fill_contents() {
void __libc_init_scudo() {
// TODO(b/158870657) make this unconditional when all devices support SCUDO.
#if defined(USE_SCUDO)
#if defined(SCUDO_PATTERN_FILL_CONTENTS)
@ -95,6 +95,7 @@ static void __libc_init_malloc_fill_contents() {
scudo_malloc_set_zero_contents(1);
#endif
#endif
SetDefaultHeapTaggingLevel();
}
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
@ -119,9 +120,6 @@ void __libc_init_common() {
__system_properties_init(); // Requires 'environ'.
__libc_init_fdsan(); // Requires system properties (for debug.fdsan).
__libc_init_fdtrack();
__libc_init_malloc_fill_contents();
SetDefaultHeapTaggingLevel();
}
void __libc_init_fork_handler() {

View file

@ -28,6 +28,7 @@
#pragma once
#include <stdint.h>
#include <sys/cdefs.h>
typedef void init_func_t(int, char*[], char*[]);
@ -57,6 +58,8 @@ __LIBC_HIDDEN__ void __libc_init_globals();
__LIBC_HIDDEN__ void __libc_init_common();
__LIBC_HIDDEN__ void __libc_init_scudo();
__LIBC_HIDDEN__ void __libc_init_AT_SECURE(char** envp);
// The fork handler must be initialised after __libc_init_malloc, as

View file

@ -90,6 +90,7 @@ static void __libc_preinit_impl() {
__libc_init_globals();
__libc_init_common();
__libc_init_scudo();
// Hooks for various libraries to let them know that we're starting up.
__libc_globals.mutate(__libc_init_malloc);

View file

@ -44,6 +44,8 @@
#include "private/bionic_elf_tls.h"
#include "private/bionic_globals.h"
#include "platform/bionic/macros.h"
#include "private/bionic_asm.h"
#include "private/bionic_asm_note.h"
#include "private/bionic_tls.h"
#include "private/KernelArgumentBlock.h"
@ -158,6 +160,94 @@ static void layout_static_tls(KernelArgumentBlock& args) {
layout.finish_layout();
}
#ifdef __aarch64__
static bool __read_memtag_note(const ElfW(Nhdr)* note, const char* name, const char* desc,
unsigned* result) {
if (note->n_namesz != 8 || strncmp(name, "Android", 8) != 0) {
return false;
}
if (note->n_type != NT_TYPE_MEMTAG) {
return false;
}
if (note->n_descsz != 4) {
async_safe_fatal("unrecognized android.memtag note: n_descsz = %d, expected 4", note->n_descsz);
}
*result = *reinterpret_cast<const ElfW(Word)*>(desc);
return true;
}
static unsigned __get_memtag_note(const ElfW(Phdr)* phdr_start, size_t phdr_ct,
const ElfW(Addr) load_bias) {
for (size_t i = 0; i < phdr_ct; ++i) {
const ElfW(Phdr)* phdr = &phdr_start[i];
if (phdr->p_type != PT_NOTE) {
continue;
}
ElfW(Addr) p = load_bias + phdr->p_vaddr;
ElfW(Addr) note_end = load_bias + phdr->p_vaddr + phdr->p_memsz;
while (p + sizeof(ElfW(Nhdr)) <= note_end) {
const ElfW(Nhdr)* note = reinterpret_cast<const ElfW(Nhdr)*>(p);
p += sizeof(ElfW(Nhdr));
const char* name = reinterpret_cast<const char*>(p);
p += align_up(note->n_namesz, 4);
const char* desc = reinterpret_cast<const char*>(p);
p += align_up(note->n_descsz, 4);
if (p > note_end) {
break;
}
unsigned ret;
if (__read_memtag_note(note, name, desc, &ret)) {
return ret;
}
}
}
return 0;
}
// Figure out the desired memory tagging mode (sync/async, heap/globals/stack) for this executable.
// This function is called from the linker before the main executable is relocated.
__attribute__((no_sanitize("hwaddress", "memtag"))) void __libc_init_mte(const void* phdr_start,
size_t phdr_ct,
uintptr_t load_bias) {
unsigned v =
__get_memtag_note(reinterpret_cast<const ElfW(Phdr)*>(phdr_start), phdr_ct, load_bias);
if (v & ~(NT_MEMTAG_LEVEL_MASK | NT_MEMTAG_HEAP)) {
async_safe_fatal("unrecognized android.memtag note: desc = %d", v);
}
if (v & NT_MEMTAG_HEAP) {
unsigned memtag_level = v & NT_MEMTAG_LEVEL_MASK;
unsigned long arg = PR_TAGGED_ADDR_ENABLE | (0xfffe << PR_MTE_TAG_SHIFT);
HeapTaggingLevel level;
switch (memtag_level) {
case NT_MEMTAG_LEVEL_ASYNC:
arg |= PR_MTE_TCF_ASYNC;
level = M_HEAP_TAGGING_LEVEL_ASYNC;
break;
case NT_MEMTAG_LEVEL_DEFAULT:
case NT_MEMTAG_LEVEL_SYNC:
arg |= PR_MTE_TCF_SYNC;
level = M_HEAP_TAGGING_LEVEL_SYNC;
break;
default:
async_safe_fatal("unrecognized android.memtag note: level = %d", memtag_level);
}
if (prctl(PR_SET_TAGGED_ADDR_CTRL, arg, 0, 0, 0) == 0) {
__libc_shared_globals()->initial_heap_tagging_level = level;
return;
}
}
if (prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == 0) {
__libc_shared_globals()->initial_heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
}
}
#else // __aarch64__
void __libc_init_mte(const void*, size_t, uintptr_t) {}
#endif // __aarch64__
__noreturn static void __real_libc_init(void *raw_args,
void (*onexit)(void) __unused,
int (*slingshot)(int, char**, char**),
@ -175,6 +265,9 @@ __noreturn static void __real_libc_init(void *raw_args,
layout_static_tls(args);
__libc_init_main_thread_final();
__libc_init_common();
__libc_init_mte(reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR)), getauxval(AT_PHNUM),
/*load_bias = */ 0);
__libc_init_scudo();
__libc_init_fork_handler();
call_ifunc_resolvers();

View file

@ -69,4 +69,10 @@
.long (__bionic_asm_aarch64_feature_pac | \
__bionic_asm_aarch64_feature_bti); \
.long 0; \
.popsection; \
.popsection;
#define NT_MEMTAG_LEVEL_MASK 3
#define NT_MEMTAG_LEVEL_DEFAULT 0
#define NT_MEMTAG_LEVEL_ASYNC 1
#define NT_MEMTAG_LEVEL_SYNC 2
#define NT_MEMTAG_HEAP 4

View file

@ -30,3 +30,4 @@
#define NT_TYPE_IDENT 1
#define NT_TYPE_KUSER 3
#define NT_TYPE_MEMTAG 4

View file

@ -41,6 +41,8 @@
#include "private/bionic_vdso.h"
#include "private/WriteProtected.h"
#include <platform/bionic/malloc.h>
struct libc_globals {
vdso_entry vdso[VDSO_END];
long setjmp_cookie;
@ -106,6 +108,8 @@ struct libc_shared_globals {
const char* scudo_stack_depot = nullptr;
const char* scudo_region_info = nullptr;
HeapTaggingLevel initial_heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
};
__LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals();

View file

@ -66,6 +66,8 @@ static void get_elf_base_from_phdr(const ElfW(Phdr)* phdr_table, size_t phdr_cou
static void set_bss_vma_name(soinfo* si);
void __libc_init_mte(const void* phdr_start, size_t phdr_count, uintptr_t load_bias);
// These should be preserved static to avoid emitting
// RELATIVE relocations for the part of the code running
// before linker links itself.
@ -403,6 +405,8 @@ static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load
strerror(errno));
}
}
__libc_init_mte(somain->phdr, somain->phnum, somain->load_bias);
#endif
// Register the main executable and the linker upfront to have