From: Victor Stinner Date: Wed, 2 Mar 2022 17:12:26 +0000 (+0100) Subject: [3.10] bpo-46633: Skip tests on ASAN and/or MSAN builds (GH-31632) (GH-31634) (GH... X-Git-Tag: v3.9.11~43 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=359bc392ba2b8f2acca223426c8210bb74f724c6;p=thirdparty%2FPython%2Fcpython.git [3.10] bpo-46633: Skip tests on ASAN and/or MSAN builds (GH-31632) (GH-31634) (GH-31644) * Refactor sanitiser skip tests into test.support (GH-30889) * Refactor sanitizer skip tests into test.support (cherry picked from commit b1cb8430504931f7854eac5d32cba74770078a4e) * Add skips to crashing tests under sanitizers instead of manually skipping them (GH-30897) (cherry picked from commit a27505345e34d462139f5f8b6b5e7c9a59955150) * bpo-46633: Skip tests on ASAN and/or MSAN builds (GH-31632) Skip tests on ASAN and/or MSAN builds: * multiprocessing tests * test___all__ * test_concurrent_futures * test_decimal * test_peg_generator * test_tools (cherry picked from commit 9204bb72a2da5885facc747e63d2bd2d654606fe) Co-authored-by: Pablo Galindo Salgado (cherry picked from commit 93264452d952d9ba604bacf2947c2df5dd477931) --- diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 3ae0cb976863..bcbf5676b2e3 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -69,6 +69,12 @@ except ImportError: msvcrt = None +if support.check_sanitizer(address=True): + # bpo-45200: Skip multiprocessing tests if Python is built with ASAN to + # work around a libasan race condition: dead lock in pthread_create(). + raise unittest.SkipTest("libasan has a pthread_create() dead lock") + + def latin(s): return s.encode('latin') diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 53804f13fc8a..c4115a850b2e 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -54,7 +54,7 @@ __all__ = [ "requires_IEEE_754", "skip_unless_xattr", "requires_zlib", "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", "skip_if_buggy_ucrt_strfptime", - "ignore_warnings", + "ignore_warnings", "check_sanitizer", "skip_if_sanitizer", # sys "is_jython", "is_android", "check_impl_detail", "unix_shell", "setswitchinterval", @@ -644,6 +644,41 @@ def requires_mac_ver(*min_version): return decorator +def check_sanitizer(*, address=False, memory=False, ub=False): + """Returns True if Python is compiled with sanitizer support""" + if not (address or memory or ub): + raise ValueError('At least one of address, memory, or ub must be True') + + + _cflags = sysconfig.get_config_var('CFLAGS') or '' + _config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' + memory_sanitizer = ( + '-fsanitize=memory' in _cflags or + '--with-memory-sanitizer' in _config_args + ) + address_sanitizer = ( + '-fsanitize=address' in _cflags or + '--with-memory-sanitizer' in _config_args + ) + ub_sanitizer = ( + '-fsanitize=undefined' in _cflags or + '--with-undefined-behavior-sanitizer' in _config_args + ) + return ( + (memory and memory_sanitizer) or + (address and address_sanitizer) or + (ub and ub_sanitizer) + ) + + +def skip_if_sanitizer(reason=None, *, address=False, memory=False, ub=False): + """Decorator raising SkipTest if running with a sanitizer active.""" + if not reason: + reason = 'not working with sanitizers active' + skip = check_sanitizer(address=address, memory=memory, ub=ub) + return unittest.skipIf(skip, reason) + + def system_must_validate_cert(f): """Skip the test on TLS certificate validation failures.""" @functools.wraps(f) diff --git a/Lib/test/test___all__.py b/Lib/test/test___all__.py index 0ba243ee4e74..7e1f80f25125 100644 --- a/Lib/test/test___all__.py +++ b/Lib/test/test___all__.py @@ -4,6 +4,13 @@ import os import sys +if support.check_sanitizer(address=True, memory=True): + # bpo-46633: test___all__ is skipped because importing some modules + # directly can trigger known problems with ASAN (like tk or crypt). + raise unittest.SkipTest("workaround ASAN build issues on loading tests " + "like tk or crypt") + + class NoAll(RuntimeError): pass diff --git a/Lib/test/test_concurrent_futures.py b/Lib/test/test_concurrent_futures.py index d693fb4ee199..d421b02754cf 100644 --- a/Lib/test/test_concurrent_futures.py +++ b/Lib/test/test_concurrent_futures.py @@ -32,6 +32,12 @@ import multiprocessing.process import multiprocessing.util +if support.check_sanitizer(address=True, memory=True): + # bpo-46633: Skip the test because it is too slow when Python is built + # with ASAN/MSAN: between 5 and 20 minutes on GitHub Actions. + raise unittest.SkipTest("test too slow on ASAN/MSAN build") + + def create_future(state=PENDING, exception=None, result=None): f = Future() f._state = state diff --git a/Lib/test/test_crypt.py b/Lib/test/test_crypt.py index 5dc83b4ecbfa..877c575c5534 100644 --- a/Lib/test/test_crypt.py +++ b/Lib/test/test_crypt.py @@ -1,8 +1,11 @@ import sys import unittest +from test.support import check_sanitizer try: + if check_sanitizer(address=True, memory=True): + raise unittest.SkipTest("The crypt module SEGFAULTs on ASAN/MSAN builds") import crypt IMPORT_ERROR = None except ImportError as ex: diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 3f30a935d583..58f4df306016 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -33,24 +33,14 @@ import unittest import numbers import locale from test.support import (run_unittest, run_doctest, is_resource_enabled, - requires_IEEE_754, requires_docstrings) -from test.support import (import_fresh_module, TestFailed, + requires_IEEE_754, requires_docstrings, + import_fresh_module, TestFailed, run_with_locale, cpython_only, - darwin_malloc_err_warning) + darwin_malloc_err_warning, + check_sanitizer) import random import inspect import threading -import sysconfig -_cflags = sysconfig.get_config_var('CFLAGS') or '' -_config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' -MEMORY_SANITIZER = ( - '-fsanitize=memory' in _cflags or - '--with-memory-sanitizer' in _config_args -) - -ADDRESS_SANITIZER = ( - '-fsanitize=address' in _cflags -) if sys.platform == 'darwin': @@ -5497,7 +5487,8 @@ class CWhitebox(unittest.TestCase): # Issue 41540: @unittest.skipIf(sys.platform.startswith("aix"), "AIX: default ulimit: test is flaky because of extreme over-allocation") - @unittest.skipIf(MEMORY_SANITIZER or ADDRESS_SANITIZER, "sanitizer defaults to crashing " + @unittest.skipIf(check_sanitizer(address=True, memory=True), + "ASAN/MSAN sanitizer defaults to crashing " "instead of returning NULL for malloc failure.") def test_maxcontext_exact_arith(self): diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index c64afe88c25d..2f8f5eec475f 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -5,9 +5,9 @@ import os import signal import subprocess import sys -import sysconfig from test import support from test.support import script_helper, is_android +from test.support import skip_if_sanitizer import tempfile import unittest from textwrap import dedent @@ -19,16 +19,6 @@ except ImportError: TIMEOUT = 0.5 MS_WINDOWS = (os.name == 'nt') -_cflags = sysconfig.get_config_var('CFLAGS') or '' -_config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' -UB_SANITIZER = ( - '-fsanitize=undefined' in _cflags or - '--with-undefined-behavior-sanitizer' in _config_args -) -MEMORY_SANITIZER = ( - '-fsanitize=memory' in _cflags or - '--with-memory-sanitizer' in _config_args -) def expected_traceback(lineno1, lineno2, header, min_count=1): @@ -271,8 +261,8 @@ class FaultHandlerTests(unittest.TestCase): 3, 'Segmentation fault') - @unittest.skipIf(UB_SANITIZER or MEMORY_SANITIZER, - "sanitizer builds change crashing process output.") + @skip_if_sanitizer(memory=True, ub=True, reason="sanitizer " + "builds change crashing process output.") @skip_segfault_on_android def test_enable_file(self): with temporary_filename() as filename: @@ -288,8 +278,8 @@ class FaultHandlerTests(unittest.TestCase): @unittest.skipIf(sys.platform == "win32", "subprocess doesn't support pass_fds on Windows") - @unittest.skipIf(UB_SANITIZER or MEMORY_SANITIZER, - "sanitizer builds change crashing process output.") + @skip_if_sanitizer(memory=True, ub=True, reason="sanitizer " + "builds change crashing process output.") @skip_segfault_on_android def test_enable_fd(self): with tempfile.TemporaryFile('wb+') as fp: diff --git a/Lib/test/test_idle.py b/Lib/test/test_idle.py index 310b72c1d72e..c747b02f6430 100644 --- a/Lib/test/test_idle.py +++ b/Lib/test/test_idle.py @@ -1,5 +1,9 @@ import unittest from test.support import import_module +from test.support import check_sanitizer + +if check_sanitizer(address=True, memory=True): + raise unittest.SkipTest("Tests involvin libX11 can SEGFAULT on ASAN/MSAN builds") # Skip test_idle if _tkinter wasn't built, if tkinter is missing, # if tcl/tk is not the 8.5+ needed for ttk widgets, diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py index 49463e54e3c7..feee861830e4 100644 --- a/Lib/test/test_io.py +++ b/Lib/test/test_io.py @@ -28,7 +28,6 @@ import pickle import random import signal import sys -import sysconfig import textwrap import threading import time @@ -40,7 +39,7 @@ from itertools import cycle, count from test import support from test.support.script_helper import ( assert_python_ok, assert_python_failure, run_python_until_end) -from test.support import FakePath +from test.support import FakePath, skip_if_sanitizer import codecs import io # C implementation of io @@ -62,17 +61,6 @@ else: class EmptyStruct(ctypes.Structure): pass -_cflags = sysconfig.get_config_var('CFLAGS') or '' -_config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' -MEMORY_SANITIZER = ( - '-fsanitize=memory' in _cflags or - '--with-memory-sanitizer' in _config_args -) - -ADDRESS_SANITIZER = ( - '-fsanitize=address' in _cflags -) - # Does io.IOBase finalizer log the exception if the close() method fails? # The exception is ignored silently by default in release build. IOBASE_EMITS_UNRAISABLE = (hasattr(sys, "gettotalrefcount") or sys.flags.dev_mode) @@ -1543,8 +1531,8 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests): class CBufferedReaderTest(BufferedReaderTest, SizeofTest): tp = io.BufferedReader - @unittest.skipIf(MEMORY_SANITIZER or ADDRESS_SANITIZER, "sanitizer defaults to crashing " - "instead of returning NULL for malloc failure.") + @skip_if_sanitizer(memory=True, address=True, reason= "sanitizer defaults to crashing " + "instead of returning NULL for malloc failure.") def test_constructor(self): BufferedReaderTest.test_constructor(self) # The allocation can succeed on 32-bit builds, e.g. with more @@ -1892,8 +1880,8 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests): class CBufferedWriterTest(BufferedWriterTest, SizeofTest): tp = io.BufferedWriter - @unittest.skipIf(MEMORY_SANITIZER or ADDRESS_SANITIZER, "sanitizer defaults to crashing " - "instead of returning NULL for malloc failure.") + @skip_if_sanitizer(memory=True, address=True, reason= "sanitizer defaults to crashing " + "instead of returning NULL for malloc failure.") def test_constructor(self): BufferedWriterTest.test_constructor(self) # The allocation can succeed on 32-bit builds, e.g. with more @@ -2391,8 +2379,8 @@ class BufferedRandomTest(BufferedReaderTest, BufferedWriterTest): class CBufferedRandomTest(BufferedRandomTest, SizeofTest): tp = io.BufferedRandom - @unittest.skipIf(MEMORY_SANITIZER or ADDRESS_SANITIZER, "sanitizer defaults to crashing " - "instead of returning NULL for malloc failure.") + @skip_if_sanitizer(memory=True, address=True, reason= "sanitizer defaults to crashing " + "instead of returning NULL for malloc failure.") def test_constructor(self): BufferedRandomTest.test_constructor(self) # The allocation can succeed on 32-bit builds, e.g. with more diff --git a/Lib/test/test_peg_generator/__init__.py b/Lib/test/test_peg_generator/__init__.py index fa855f2104c5..77f72fcc7c6e 100644 --- a/Lib/test/test_peg_generator/__init__.py +++ b/Lib/test/test_peg_generator/__init__.py @@ -1,7 +1,15 @@ -import os - +import os.path +import unittest +from test import support from test.support import load_package_tests + +if support.check_sanitizer(address=True, memory=True): + # bpo-46633: Skip the test because it is too slow when Python is built + # with ASAN/MSAN: between 5 and 20 minutes on GitHub Actions. + raise unittest.SkipTest("test too slow on ASAN/MSAN build") + + # Load all tests in package def load_tests(*args): return load_package_tests(os.path.dirname(__file__), *args) diff --git a/Lib/test/test_tix.py b/Lib/test/test_tix.py index e6ea3d07444c..9dbf9b240b4f 100644 --- a/Lib/test/test_tix.py +++ b/Lib/test/test_tix.py @@ -1,7 +1,12 @@ import unittest from test import support +from test.support import check_sanitizer import sys +if check_sanitizer(address=True, memory=True): + raise unittest.SkipTest("Tests involvin libX11 can SEGFAULT on ASAN/MSAN builds") + + # Skip this test if the _tkinter module wasn't built. _tkinter = support.import_module('_tkinter') diff --git a/Lib/test/test_tk.py b/Lib/test/test_tk.py index d45acbfc6e1a..577ed133ee90 100644 --- a/Lib/test/test_tk.py +++ b/Lib/test/test_tk.py @@ -1,4 +1,10 @@ +import unittest from test import support +from test.support import check_sanitizer + +if check_sanitizer(address=True, memory=True): + raise unittest.SkipTest("Tests involvin libX11 can SEGFAULT on ASAN/MSAN builds") + # Skip test if _tkinter wasn't built. support.import_module('_tkinter') diff --git a/Lib/test/test_tools/__init__.py b/Lib/test/test_tools/__init__.py index eb9acad677d5..3b5c5b2df5f1 100644 --- a/Lib/test/test_tools/__init__.py +++ b/Lib/test/test_tools/__init__.py @@ -5,6 +5,13 @@ import os.path import unittest from test import support + +if support.check_sanitizer(address=True, memory=True): + # bpo-46633: Skip the test because it is too slow when Python is built + # with ASAN/MSAN: between 5 and 20 minutes on GitHub Actions. + raise unittest.SkipTest("test too slow on ASAN/MSAN build") + + basepath = os.path.normpath( os.path.dirname( # os.path.dirname( # Lib diff --git a/Lib/test/test_ttk_guionly.py b/Lib/test/test_ttk_guionly.py index 0b57d5a0d6c2..e62636637c35 100644 --- a/Lib/test/test_ttk_guionly.py +++ b/Lib/test/test_ttk_guionly.py @@ -1,5 +1,9 @@ import unittest from test import support +from test.support import check_sanitizer + +if check_sanitizer(address=True, memory=True): + raise unittest.SkipTest("Tests involvin libX11 can SEGFAULT on ASAN/MSAN builds") # Skip this test if _tkinter wasn't built. support.import_module('_tkinter')