88import sys
99from contextlib import suppress
1010from logging import getLogger
11+ from mmap import ACCESS_READ ,mmap
1112from os import environ
1213from pathlib import Path
1314from subprocess import STDOUT ,CalledProcessError ,check_output
2829else :
2930IS_WINDOWS = False
3031
32+ __author__ = "Tyson Smith"
33+
3134CERTUTIL = "certutil.exe" if IS_WINDOWS else "certutil"
3235LOG = getLogger (__name__ )
3336
34- __author__ = "Tyson Smith"
35-
3637
3738def _configure_sanitizers (
38- orig_env :Mapping [str ,str ],log_path :Path
39+ orig_env :Mapping [str ,str ],
40+ log_path :Path ,
41+ symbolize :bool = False ,
3942)-> dict [str ,str ]:
4043"""Copy environment and update default values in *SAN_OPTIONS entries.
4144 These values are only updated if they are not provided, with the exception of
@@ -44,13 +47,15 @@ def _configure_sanitizers(
4447 Args:
4548 orig_env: Current environment.
4649 log_path: Location to write sanitizer logs to.
50+ symbolize: Enable automatic symbolizing. This should only used when required to
51+ minimize memory usage.
4752
4853 Returns:
4954 Environment with *SAN_OPTIONS defaults set.
5055 """
5156env = dict (orig_env )
5257# https://github.com/google/sanitizers/wiki/SanitizerCommonFlags
53- common_flags = [
58+ common_flags = (
5459 ("abort_on_error" ,"false" ),
5560 ("allocator_may_return_null" ,"true" ),
5661 ("disable_coredump" ,"true" ),
@@ -64,10 +69,7 @@ def _configure_sanitizers(
6469 ("handle_sigfpe" ,"true" ),
6570# set to be safe
6671 ("handle_sigill" ,"true" ),
67- # do not automatically symbolize
68- # this should be done after to avoid hitting memory limitations
69- ("symbolize" ,"false" ),
70- ]
72+ )
7173
7274# setup Address Sanitizer options ONLY if not set manually in environment
7375# https://github.com/google/sanitizers/wiki/AddressSanitizerFlags
@@ -99,6 +101,7 @@ def _configure_sanitizers(
99101asan_config .add ("strict_init_order" ,"true" )
100102# temporarily revert to default (false) until https://bugzil.la/1767068 is fixed
101103# asan_config.add("strict_string_checks", "true")
104+ asan_config .add ("symbolize" ,"1" if symbolize else "0" )
102105env ["ASAN_OPTIONS" ]= str (asan_config )
103106
104107# setup Leak Sanitizer options ONLY if not set manually in environment
@@ -126,6 +129,7 @@ def _configure_sanitizers(
126129tsan_config .add ("log_path" ,f"'{ log_path } '" ,overwrite = True )
127130# This is an experimental feature added in Bug 1792757
128131tsan_config .add ("rss_limit_heap_profile" ,"true" )
132+ tsan_config .add ("symbolize" ,"1" if symbolize else "0" )
129133env ["TSAN_OPTIONS" ]= str (tsan_config )
130134
131135# setup Undefined Behavior Sanitizer options ONLY if not set manually in environment
@@ -140,6 +144,7 @@ def _configure_sanitizers(
140144ubsan_config .add ("log_path" ,f"'{ log_path } '" ,overwrite = True )
141145ubsan_config .add ("print_stacktrace" ,"1" )
142146ubsan_config .add ("report_error_type" ,"1" )
147+ ubsan_config .add ("symbolize" ,"1" if symbolize else "0" )
143148env ["UBSAN_OPTIONS" ]= str (ubsan_config )
144149
145150return env
@@ -186,6 +191,28 @@ def certutil_find(browser_bin: Path | None = None) -> str:
186191return CERTUTIL
187192
188193
194+ def detect_sanitizer (binary :Path )-> str | None :
195+ """Detect sanitizer instrumentation in browser build.
196+
197+ Args:
198+ binary: Location of browser binary.
199+
200+ Returns:
201+ Name of sanitizer in use or None.
202+ """
203+ with (
204+ binary .open ("rb" )as bin_fp ,
205+ mmap (bin_fp .fileno (),0 ,access = ACCESS_READ )as bmm ,
206+ ):
207+ if bmm .find (b"__tsan_" )!= - 1 :
208+ return "tsan"
209+ if bmm .find (b"__asan_" )!= - 1 :
210+ return "asan"
211+ if bmm .find (b"__ubsan_" )!= - 1 :
212+ return "ubsan"
213+ return None
214+
215+
189216def files_in_use (files :Iterable [Path ])-> Generator [tuple [Path ,int ,str ]]:
190217"""Check if any of the given files are open.
191218 WARNING: This can be slow on Windows.
@@ -226,6 +253,7 @@ def files_in_use(files: Iterable[Path]) -> Generator[tuple[Path, int, str]]:
226253def prepare_environment (
227254sanitizer_log :Path ,
228255env_mod :Mapping [str ,str | None ]| None = None ,
256+ sanitizer :str | None = None ,
229257)-> dict [str ,str ]:
230258"""Create environment that can be used when launching the browser.
231259
@@ -235,6 +263,7 @@ def prepare_environment(
235263 env_mod: Environment modifier. Add, remove and update entries
236264 in the prepared environment. Add/update by setting
237265 value or remove entry by setting value to None.
266+ sanitizer: Sanitizer in use.
238267
239268 Returns:
240269 Environment to use when launching browser.
@@ -302,7 +331,9 @@ def prepare_environment(
302331env .pop ("MOZ_CRASHREPORTER_NO_REPORT" ,None )
303332env .pop ("MOZ_CRASHREPORTER_SHUTDOWN" ,None )
304333
305- env = _configure_sanitizers (env ,sanitizer_log )
334+ # automatically symbolize traces when TSan is in use
335+ # it is required for runtime TSan suppressions
336+ env = _configure_sanitizers (env ,sanitizer_log ,symbolize = sanitizer == "tsan" )
306337# filter environment to avoid leaking sensitive information
307338return {k :v for k ,v in env .items ()if "_SECRET" not in k }
308339