remap_alloc.cc (7915B)
1 // Copyright 2021 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifdef UNSAFE_BUFFERS_BUILD 6 // TODO(crbug.com/390223051): Remove C-library calls to fix the errors. 7 #pragma allow_unsafe_libc_calls 8 #endif 9 10 #include <cstddef> 11 #include <cstring> 12 13 #include "build/build_config.h" 14 #include "build/rust/std/alias.h" 15 #include "build/rust/std/buildflags.h" 16 #include "build/rust/std/immediate_crash.h" 17 18 #if BUILDFLAG(RUST_ALLOCATOR_USES_PARTITION_ALLOC) 19 #include "partition_alloc/partition_alloc_constants.h" // nogncheck 20 #include "partition_alloc/shim/allocator_shim.h" // nogncheck 21 #elif BUILDFLAG(IS_WIN) 22 #include <cstdlib> 23 #endif 24 25 // When linking a final binary, rustc has to pick between either: 26 // * The default Rust allocator 27 // * Any #[global_allocator] defined in *any rlib in its dependency tree* 28 // (https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html) 29 // 30 // In this latter case, this fact will be recorded in some of the metadata 31 // within the .rlib file. (An .rlib file is just a .a file, but does have 32 // additional metadata for use by rustc. This is, as far as I know, the only 33 // such metadata we would ideally care about.) 34 // 35 // In all the linked rlibs, 36 // * If 0 crates define a #[global_allocator], rustc uses its default allocator 37 // * If 1 crate defines a #[global_allocator], rustc uses that 38 // * If >1 crates define a #[global_allocator], rustc bombs out. 39 // 40 // Because rustc does these checks, it doesn't just have the __rust_alloc 41 // symbols defined anywhere (neither in the stdlib nor in any of these 42 // crates which have a #[global_allocator] defined.) 43 // 44 // Instead: 45 // Rust's final linking stage invokes dynamic LLVM codegen to create symbols 46 // for the basic heap allocation operations. It literally creates a 47 // __rust_alloc symbol at link time. Unless any crate has specified a 48 // #[global_allocator], it simply calls from __rust_alloc into 49 // __rdl_alloc, which is the default Rust allocator. The same applies to a 50 // few other symbols. 51 // 52 // We're not (always) using rustc for final linking. For cases where we're not 53 // Rustc as the final linker, we'll define those symbols here instead. This 54 // allows us to redirect allocation to PartitionAlloc if clang is doing the 55 // link. 56 // 57 // We use unchecked allocation paths in PartitionAlloc rather than going through 58 // its shims in `malloc()` etc so that we can support fallible allocation paths 59 // such as Vec::try_reserve without crashing on allocation failure. 60 // 61 // In future, we should build a crate with a #[global_allocator] and 62 // redirect these symbols back to Rust in order to use to that crate instead. 63 // This would allow Rust-linked executables to: 64 // 1. Use PartitionAlloc on Windows. The stdlib uses Windows heap functions 65 // directly that PartitionAlloc can not intercept. 66 // 2. Have `Vec::try_reserve` to fail at runtime on Linux instead of crashing in 67 // malloc() where PartitionAlloc replaces that function. 68 // 69 // They're weak symbols, because this file will sometimes end up in targets 70 // which are linked by rustc, and thus we would otherwise get duplicate 71 // definitions. The following definitions will therefore only end up being 72 // used in targets which are linked by our C++ toolchain. 73 // 74 // # On Windows ASAN 75 // 76 // In ASAN builds, PartitionAlloc-Everywhere is disabled, meaning malloc() and 77 // friends in C++ do not go to PartitionAlloc. So we also don't point the Rust 78 // allocation functions at PartitionAlloc. Generally, this means we just direct 79 // them to the Standard Library's allocator. 80 // 81 // However, on Windows the Standard Library uses HeapAlloc() and Windows ASAN 82 // does *not* hook that method, so ASAN does not get to hear about allocations 83 // made in Rust. To resolve this, we redirect allocation to _aligned_malloc 84 // which Windows ASAN *does* hook. 85 // 86 // Note that there is a runtime option to make ASAN hook HeapAlloc() but 87 // enabling it breaks Win32 APIs like CreateProcess: 88 // https://issues.chromium.org/u/1/issues/368070343#comment29 89 90 extern "C" { 91 92 #ifdef COMPONENT_BUILD 93 #if BUILDFLAG(IS_WIN) 94 #define REMAP_ALLOC_ATTRIBUTES __declspec(dllexport) __attribute__((weak)) 95 #else 96 #define REMAP_ALLOC_ATTRIBUTES \ 97 __attribute__((visibility("default"))) __attribute__((weak)) 98 #endif 99 #else 100 #define REMAP_ALLOC_ATTRIBUTES __attribute__((weak)) 101 #endif // COMPONENT_BUILD 102 103 #if !BUILDFLAG(RUST_ALLOCATOR_USES_PARTITION_ALLOC) && BUILDFLAG(IS_WIN) && \ 104 defined(ADDRESS_SANITIZER) 105 #define USE_WIN_ALIGNED_MALLOC 1 106 #else 107 #define USE_WIN_ALIGNED_MALLOC 0 108 #endif 109 110 // This must exist as the stdlib depends on it to prove that we know the 111 // alloc shims below are unstable. In the future we may be required to replace 112 // them with a #[global_allocator] crate (see file comment above for more). 113 // 114 // Marked as weak as when Rust drives linking it includes this symbol itself, 115 // and we don't want a collision due to C++ being in the same link target, where 116 // C++ causes us to explicitly link in the stdlib and this symbol here. 117 [[maybe_unused]] 118 __attribute__((weak)) unsigned char __rust_no_alloc_shim_is_unstable; 119 120 REMAP_ALLOC_ATTRIBUTES void* __rust_alloc(size_t size, size_t align) { 121 #if BUILDFLAG(RUST_ALLOCATOR_USES_PARTITION_ALLOC) 122 // PartitionAlloc will crash if given an alignment larger than this. 123 if (align > partition_alloc::internal::kMaxSupportedAlignment) { 124 return nullptr; 125 } 126 127 if (align <= alignof(std::max_align_t)) { 128 return allocator_shim::UncheckedAlloc(size); 129 } else { 130 return allocator_shim::UncheckedAlignedAlloc(size, align); 131 } 132 #elif USE_WIN_ALIGNED_MALLOC 133 return _aligned_malloc(size, align); 134 #else 135 extern void* __rdl_alloc(size_t size, size_t align); 136 return __rdl_alloc(size, align); 137 #endif 138 } 139 140 REMAP_ALLOC_ATTRIBUTES void __rust_dealloc(void* p, size_t size, size_t align) { 141 #if BUILDFLAG(RUST_ALLOCATOR_USES_PARTITION_ALLOC) 142 if (align <= alignof(std::max_align_t)) { 143 allocator_shim::UncheckedFree(p); 144 } else { 145 allocator_shim::UncheckedAlignedFree(p); 146 } 147 #elif USE_WIN_ALIGNED_MALLOC 148 return _aligned_free(p); 149 #else 150 extern void __rdl_dealloc(void* p, size_t size, size_t align); 151 __rdl_dealloc(p, size, align); 152 #endif 153 } 154 155 REMAP_ALLOC_ATTRIBUTES void* __rust_realloc(void* p, 156 size_t old_size, 157 size_t align, 158 size_t new_size) { 159 #if BUILDFLAG(RUST_ALLOCATOR_USES_PARTITION_ALLOC) 160 if (align <= alignof(std::max_align_t)) { 161 return allocator_shim::UncheckedRealloc(p, new_size); 162 } else { 163 return allocator_shim::UncheckedAlignedRealloc(p, new_size, align); 164 } 165 #elif USE_WIN_ALIGNED_MALLOC 166 return _aligned_realloc(p, new_size, align); 167 #else 168 extern void* __rdl_realloc(void* p, size_t old_size, size_t align, 169 size_t new_size); 170 return __rdl_realloc(p, old_size, align, new_size); 171 #endif 172 } 173 174 REMAP_ALLOC_ATTRIBUTES void* __rust_alloc_zeroed(size_t size, size_t align) { 175 #if BUILDFLAG(RUST_ALLOCATOR_USES_PARTITION_ALLOC) || USE_WIN_ALIGNED_MALLOC 176 // TODO(danakj): When RUST_ALLOCATOR_USES_PARTITION_ALLOC is true, it's 177 // possible that a partition_alloc::UncheckedAllocZeroed() call would perform 178 // better than partition_alloc::UncheckedAlloc() + memset. But there is no 179 // such API today. See b/342251590. 180 void* p = __rust_alloc(size, align); 181 if (p) { 182 memset(p, 0, size); 183 } 184 return p; 185 #else 186 extern void* __rdl_alloc_zeroed(size_t size, size_t align); 187 return __rdl_alloc_zeroed(size, align); 188 #endif 189 } 190 191 REMAP_ALLOC_ATTRIBUTES void __rust_alloc_error_handler(size_t size, 192 size_t align) { 193 NO_CODE_FOLDING(); 194 IMMEDIATE_CRASH(); 195 } 196 197 REMAP_ALLOC_ATTRIBUTES extern const unsigned char 198 __rust_alloc_error_handler_should_panic = 0; 199 200 } // extern "C"