tor-browser

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

llvmorg-21-init-17702-g0d7e64f5d2b4.patch (12525B)


      1 From 7eb91b30bedfda404e7b68d94e7ab770b774846a Mon Sep 17 00:00:00 2001
      2 From: David Justo <david.justo.1996@gmail.com>
      3 Date: Wed, 2 Jul 2025 15:37:28 -0700
      4 Subject: [PATCH] [ASan][Windows] Honor asan config flags on windows when set
      5 through the user function (#122990)
      6 
      7 **Related to:** https://github.com/llvm/llvm-project/issues/117925
      8 **Follow up to:** https://github.com/llvm/llvm-project/pull/117929
      9 
     10 **Context:**
     11 As noted in the linked issue, some ASan configuration flags are not
     12 honored on Windows when set through the `__asan_default_options` user
     13 function. The reason for this is that `__asan_default_options` is not
     14 available by the time `AsanInitInternal` executes, which is responsible
     15 for applying the ASan flags.
     16 
     17 To fix this properly, we'll probably need a deep re-design of ASan
     18 initialization so that it is consistent across OS'es.
     19 In the meantime, this PR offers a practical workaround.
     20 
     21 **This PR:** refactors part of `AsanInitInternal` so that **idempotent**
     22 flag-applying steps are extracted into a new function `ApplyOptions`.
     23 This function is **also** invoked in the "weak function callback" on
     24 Windows (which gets called when `__asan_default_options` is available)
     25 so that, if any flags were set through the user-function, they are
     26 safely applied _then_.
     27 
     28 Today, `ApplyOptions` contains only a subset of flags. My hope is that
     29 `ApplyOptions` will over time, through incremental refactorings
     30 `AsanInitInternal` so that **all** flags are eventually honored.
     31 
     32 Other minor changes:
     33 * The introduction of a `ApplyAllocatorOptions` helper method, needed to
     34 implement `ApplyOptions` for allocator options without re-initializing
     35 the entire allocator. Reinitializing the entire allocator is expensive,
     36 as it may do a whole pass over all the marked memory. To my knowledge,
     37 this isn't needed for the options captured in `ApplyAllocatorOptions`.
     38 * Rename `ProcessFlags` to `ValidateFlags`, which seems like a more
     39 accurate name to what that function does, and prevents confusion when
     40 compared to the new `ApplyOptions` function.
     41 ---
     42 compiler-rt/lib/asan/asan_allocator.cpp       | 12 ++++-
     43 compiler-rt/lib/asan/asan_allocator.h         |  1 +
     44 compiler-rt/lib/asan/asan_flags.cpp           | 32 +++++-------
     45 compiler-rt/lib/asan/asan_internal.h          |  1 +
     46 compiler-rt/lib/asan/asan_rtl.cpp             | 52 +++++++++++++++----
     47 .../Windows/alloc_dealloc_mismatch.cpp        | 29 +++++++++++
     48 6 files changed, 98 insertions(+), 29 deletions(-)
     49 create mode 100644 compiler-rt/test/asan/TestCases/Windows/alloc_dealloc_mismatch.cpp
     50 
     51 diff --git a/compiler-rt/lib/asan/asan_allocator.cpp b/compiler-rt/lib/asan/asan_allocator.cpp
     52 index 3a55c2af6565..d3c0288285b8 100644
     53 --- a/compiler-rt/lib/asan/asan_allocator.cpp
     54 +++ b/compiler-rt/lib/asan/asan_allocator.cpp
     55 @@ -424,10 +424,15 @@ struct Allocator {
     56     PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
     57   }
     58 
     59 -  void ReInitialize(const AllocatorOptions &options) {
     60 +  // Apply provided AllocatorOptions to an Allocator
     61 +  void ApplyOptions(const AllocatorOptions &options) {
     62     SetAllocatorMayReturnNull(options.may_return_null);
     63     allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms);
     64     SharedInitCode(options);
     65 +  }
     66 +
     67 +  void ReInitialize(const AllocatorOptions &options) {
     68 +    ApplyOptions(options);
     69 
     70     // Poison all existing allocation's redzones.
     71     if (CanPoisonMemory()) {
     72 @@ -977,6 +982,11 @@ void ReInitializeAllocator(const AllocatorOptions &options) {
     73   instance.ReInitialize(options);
     74 }
     75 
     76 +// Apply provided AllocatorOptions to an Allocator
     77 +void ApplyAllocatorOptions(const AllocatorOptions &options) {
     78 +  instance.ApplyOptions(options);
     79 +}
     80 +
     81 void GetAllocatorOptions(AllocatorOptions *options) {
     82   instance.GetOptions(options);
     83 }
     84 diff --git a/compiler-rt/lib/asan/asan_allocator.h b/compiler-rt/lib/asan/asan_allocator.h
     85 index db8dc3bebfc6..a94ef958aa75 100644
     86 --- a/compiler-rt/lib/asan/asan_allocator.h
     87 +++ b/compiler-rt/lib/asan/asan_allocator.h
     88 @@ -47,6 +47,7 @@ struct AllocatorOptions {
     89 void InitializeAllocator(const AllocatorOptions &options);
     90 void ReInitializeAllocator(const AllocatorOptions &options);
     91 void GetAllocatorOptions(AllocatorOptions *options);
     92 +void ApplyAllocatorOptions(const AllocatorOptions &options);
     93 
     94 class AsanChunkView {
     95  public:
     96 diff --git a/compiler-rt/lib/asan/asan_flags.cpp b/compiler-rt/lib/asan/asan_flags.cpp
     97 index 9cfb70bd00c7..190a89345dd1 100644
     98 --- a/compiler-rt/lib/asan/asan_flags.cpp
     99 +++ b/compiler-rt/lib/asan/asan_flags.cpp
    100 @@ -144,6 +144,7 @@ static void InitializeDefaultFlags() {
    101   DisplayHelpMessages(&asan_parser);
    102 }
    103 
    104 +// Validate flags and report incompatible configurations
    105 static void ProcessFlags() {
    106   Flags *f = flags();
    107 
    108 @@ -217,11 +218,12 @@ void InitializeFlags() {
    109   ProcessFlags();
    110 
    111 #if SANITIZER_WINDOWS
    112 -  // On Windows, weak symbols are emulated by having the user program
    113 -  // register which weak functions are defined.
    114 -  // The ASAN DLL will initialize flags prior to user module initialization,
    115 -  // so __asan_default_options will not point to the user definition yet.
    116 -  // We still want to ensure we capture when options are passed via
    117 +  // On Windows, weak symbols (such as the `__asan_default_options` function)
    118 +  // are emulated by having the user program register which weak functions are
    119 +  // defined. The ASAN DLL will initialize flags prior to user module
    120 +  // initialization, so __asan_default_options will not point to the user
    121 +  // definition yet. We still want to ensure we capture when options are passed
    122 +  // via
    123   // __asan_default_options, so we add a callback to be run
    124   // when it is registered with the runtime.
    125 
    126 @@ -232,21 +234,13 @@ void InitializeFlags() {
    127   // __sanitizer_register_weak_function.
    128   AddRegisterWeakFunctionCallback(
    129       reinterpret_cast<uptr>(__asan_default_options), []() {
    130 -        FlagParser asan_parser;
    131 -
    132 -        RegisterAsanFlags(&asan_parser, flags());
    133 -        RegisterCommonFlags(&asan_parser);
    134 -        asan_parser.ParseString(__asan_default_options());
    135 -
    136 -        DisplayHelpMessages(&asan_parser);
    137 +        // We call `InitializeDefaultFlags` again, instead of just parsing
    138 +        // `__asan_default_options` directly, to ensure that flags set through
    139 +        // `ASAN_OPTS` take precedence over those set through
    140 +        // `__asan_default_options`.
    141 +        InitializeDefaultFlags();
    142         ProcessFlags();
    143 -
    144 -        // TODO: Update other globals and data structures that may need to change
    145 -        // after initialization due to new flags potentially being set changing after
    146 -        // `__asan_default_options` is registered.
    147 -        // See GH issue 'https://github.com/llvm/llvm-project/issues/117925' for
    148 -        // details.
    149 -        SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
    150 +        ApplyFlags();
    151       });
    152 
    153 #  if CAN_SANITIZE_UB
    154 diff --git a/compiler-rt/lib/asan/asan_internal.h b/compiler-rt/lib/asan/asan_internal.h
    155 index 06dfc4b17733..464faad56f32 100644
    156 --- a/compiler-rt/lib/asan/asan_internal.h
    157 +++ b/compiler-rt/lib/asan/asan_internal.h
    158 @@ -61,6 +61,7 @@ using __sanitizer::StackTrace;
    159 
    160 void AsanInitFromRtl();
    161 bool TryAsanInitFromRtl();
    162 +void ApplyFlags();
    163 
    164 // asan_win.cpp
    165 void InitializePlatformExceptionHandlers();
    166 diff --git a/compiler-rt/lib/asan/asan_rtl.cpp b/compiler-rt/lib/asan/asan_rtl.cpp
    167 index 19c6c210b564..1564a8f5164c 100644
    168 --- a/compiler-rt/lib/asan/asan_rtl.cpp
    169 +++ b/compiler-rt/lib/asan/asan_rtl.cpp
    170 @@ -390,6 +390,39 @@ void PrintAddressSpaceLayout() {
    171           kHighShadowBeg > kMidMemEnd);
    172 }
    173 
    174 +// Apply most options specified either through the ASAN_OPTIONS
    175 +// environment variable, or through the `__asan_default_options` user function.
    176 +//
    177 +// This function may be called multiple times, once per weak reference callback
    178 +// on Windows, so it needs to be idempotent.
    179 +//
    180 +// Context:
    181 +// For maximum compatibility on Windows, it is necessary for ASan options to be
    182 +// configured/registered/applied inside this method (instead of in
    183 +// ASanInitInternal, for example). That's because, on Windows, the user-provided
    184 +// definition for `__asan_default_opts` may not be bound when `ASanInitInternal`
    185 +// is invoked (it is bound later).
    186 +//
    187 +// To work around the late binding on windows, `ApplyOptions` will be called,
    188 +// again, after binding to the user-provided `__asan_default_opts` function.
    189 +// Therefore, any flags not configured here are not guaranteed to be
    190 +// configurable through `__asan_default_opts` on Windows.
    191 +//
    192 +//
    193 +// For more details on this issue, see:
    194 +// https://github.com/llvm/llvm-project/issues/117925
    195 +void ApplyFlags() {
    196 +  SetCanPoisonMemory(flags()->poison_heap);
    197 +  SetMallocContextSize(common_flags()->malloc_context_size);
    198 +
    199 +  __asan_option_detect_stack_use_after_return =
    200 +      flags()->detect_stack_use_after_return;
    201 +
    202 +  AllocatorOptions allocator_options;
    203 +  allocator_options.SetFrom(flags(), common_flags());
    204 +  ApplyAllocatorOptions(allocator_options);
    205 +}
    206 +
    207 static bool AsanInitInternal() {
    208   if (LIKELY(AsanInited()))
    209     return true;
    210 @@ -397,8 +430,9 @@ static bool AsanInitInternal() {
    211 
    212   CacheBinaryName();
    213 
    214 -  // Initialize flags. This must be done early, because most of the
    215 -  // initialization steps look at flags().
    216 +  // Initialize flags. On Windows it also also register weak function callbacks.
    217 +  // This must be done early, because most of the initialization steps look at
    218 +  // flags().
    219   InitializeFlags();
    220 
    221   WaitForDebugger(flags()->sleep_before_init, "before init");
    222 @@ -416,9 +450,6 @@ static bool AsanInitInternal() {
    223   AsanCheckDynamicRTPrereqs();
    224   AvoidCVE_2016_2143();
    225 
    226 -  SetCanPoisonMemory(flags()->poison_heap);
    227 -  SetMallocContextSize(common_flags()->malloc_context_size);
    228 -
    229   InitializePlatformExceptionHandlers();
    230 
    231   InitializeHighMemEnd();
    232 @@ -429,10 +460,6 @@ static bool AsanInitInternal() {
    233   SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
    234 
    235   __sanitizer_set_report_path(common_flags()->log_path);
    236 -
    237 -  __asan_option_detect_stack_use_after_return =
    238 -      flags()->detect_stack_use_after_return;
    239 -
    240   __sanitizer::InitializePlatformEarly();
    241 
    242   // Setup internal allocator callback.
    243 @@ -460,6 +487,13 @@ static bool AsanInitInternal() {
    244   allocator_options.SetFrom(flags(), common_flags());
    245   InitializeAllocator(allocator_options);
    246 
    247 +  // Apply ASan flags.
    248 +  // NOTE: In order for options specified through `__asan_default_options` to be
    249 +  // honored on Windows, it is necessary for those options to be configured
    250 +  // inside the `ApplyOptions` method. See the function-level comment for
    251 +  // `ApplyFlags` for more details.
    252 +  ApplyFlags();
    253 +
    254   if (SANITIZER_START_BACKGROUND_THREAD_IN_ASAN_INTERNAL)
    255     MaybeStartBackgroudThread();
    256 
    257 diff --git a/compiler-rt/test/asan/TestCases/Windows/alloc_dealloc_mismatch.cpp b/compiler-rt/test/asan/TestCases/Windows/alloc_dealloc_mismatch.cpp
    258 new file mode 100644
    259 index 000000000000..c7d62f15c3c3
    260 --- /dev/null
    261 +++ b/compiler-rt/test/asan/TestCases/Windows/alloc_dealloc_mismatch.cpp
    262 @@ -0,0 +1,29 @@
    263 +// RUN: %clangxx_asan -O0 %s -o %t
    264 +// RUN: %env_asan_opts=alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-MISMATCH
    265 +// RUN: %env_asan_opts=alloc_dealloc_mismatch=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
    266 +
    267 +// RUN: %clangxx_asan -O0 %s -o %t -DUSER_FUNCTION
    268 +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-MISMATCH
    269 +
    270 +// It is expected that ASAN_OPTS will override the value set through the user function.
    271 +// RUN: %env_asan_opts=alloc_dealloc_mismatch=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUCCESS
    272 +
    273 +#if USER_FUNCTION
    274 +// It's important to test the `alloc_dealloc_mismatch` flag set through the user function because, on Windows,
    275 +// flags configured through the user-defined function `__asan_default_options` are not always be honored.
    276 +// See: https://github.com/llvm/llvm-project/issues/117925
    277 +extern "C" __declspec(dllexport) extern const char *__asan_default_options() {
    278 +  return "alloc_dealloc_mismatch=1";
    279 +}
    280 +#endif
    281 +
    282 +#include <cstdio>
    283 +#include <cstdlib>
    284 +
    285 +// Tests the `alloc_dealloc_mismatch` flag set both via user function and through the environment variable.
    286 +int main() {
    287 +  // In the 'CHECK-MISMATCH' case, we simply check that the AddressSanitizer reports an error.
    288 +  delete (new int[10]); // CHECK-MISMATCH: AddressSanitizer:
    289 +  printf("Success");    // CHECK-SUCCESS: Success
    290 +  return 0;
    291 +}
    292 \ No newline at end of file
    293 -- 
    294 2.51.0.1.g7a422dac74