tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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"