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 :
2930 IS_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 """
5156 env = 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(
99101 asan_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" )
102105 env ["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(
126129 tsan_config .add ("log_path" , f"'{ log_path } '" , overwrite = True )
127130 # This is an experimental feature added in Bug 1792757
128131 tsan_config .add ("rss_limit_heap_profile" , "true" )
132+ tsan_config .add ("symbolize" , "1" if symbolize else "0" )
129133 env ["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(
140144 ubsan_config .add ("log_path" , f"'{ log_path } '" , overwrite = True )
141145 ubsan_config .add ("print_stacktrace" , "1" )
142146 ubsan_config .add ("report_error_type" , "1" )
147+ ubsan_config .add ("symbolize" , "1" if symbolize else "0" )
143148 env ["UBSAN_OPTIONS" ] = str (ubsan_config )
144149
145150 return env
@@ -186,6 +191,28 @@ def certutil_find(browser_bin: Path | None = None) -> str:
186191 return 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 (
227254 sanitizer_log : Path ,
228255 env_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(
302331 env .pop ("MOZ_CRASHREPORTER_NO_REPORT" , None )
303332 env .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
307338 return {k : v for k , v in env .items () if "_SECRET" not in k }
308339
0 commit comments