summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAntoine Pitrou <antoine@python.org>2024-03-17 15:33:35 (GMT)
committerGitHub <noreply@github.com>2024-03-17 15:33:35 (GMT)
commit2ac1b48a044429d7a290310348b53a87b9f2033a (patch)
tree2afcf4d2bc3ba5c558e2a9e0fb86b37af9922801
parent2dbc77e1ece7ba5d192f9b83f84ca7f4fb9a74a3 (diff)
downloadcpython-2ac1b48a044429d7a290310348b53a87b9f2033a.zip
cpython-2ac1b48a044429d7a290310348b53a87b9f2033a.tar.gz
cpython-2ac1b48a044429d7a290310348b53a87b9f2033a.tar.bz2
[3.12] gh-112536: Add support for thread sanitizer (TSAN) (gh-112648) (#116924)
* [3.12] gh-112536: Add support for thread sanitizer (TSAN) (gh-112648) (cherry picked from commit 88cb9720001295f82c7771ab4ebf20f3cd0b31fb) * Remove doc for configure option (leave it hidden in this branch) --------- Co-authored-by: Samet YASLAN <sametyaslan@gmail.com>
-rw-r--r--Include/pyport.h5
-rw-r--r--Lib/test/libregrtest/utils.py7
-rw-r--r--Lib/test/support/__init__.py19
-rw-r--r--Lib/test/test_io.py9
-rw-r--r--Misc/NEWS.d/next/Build/2023-12-17-18-23-02.gh-issue-112536.8lr3Ep.rst1
-rwxr-xr-xconfigure25
-rw-r--r--configure.ac18
7 files changed, 74 insertions, 10 deletions
diff --git a/Include/pyport.h b/Include/pyport.h
index 35eca72..30b9c8e 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -748,6 +748,11 @@ extern char * _getpty(int *, int, mode_t, int);
# define _Py_ADDRESS_SANITIZER
# endif
# endif
+# if __has_feature(thread_sanitizer)
+# if !defined(_Py_THREAD_SANITIZER)
+# define _Py_THREAD_SANITIZER
+# endif
+# endif
#elif defined(__GNUC__)
# if defined(__SANITIZE_ADDRESS__)
# define _Py_ADDRESS_SANITIZER
diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py
index 1be5abd..25017e8 100644
--- a/Lib/test/libregrtest/utils.py
+++ b/Lib/test/libregrtest/utils.py
@@ -349,6 +349,9 @@ def get_build_info():
# --with-undefined-behavior-sanitizer
if support.check_sanitizer(ub=True):
sanitizers.append("UBSAN")
+ # --with-thread-sanitizer
+ if support.check_sanitizer(thread=True):
+ sanitizers.append("TSAN")
if sanitizers:
build.append('+'.join(sanitizers))
@@ -649,6 +652,7 @@ def display_header(use_resources: tuple[str, ...],
asan = support.check_sanitizer(address=True)
msan = support.check_sanitizer(memory=True)
ubsan = support.check_sanitizer(ub=True)
+ tsan = support.check_sanitizer(thread=True)
sanitizers = []
if asan:
sanitizers.append("address")
@@ -656,12 +660,15 @@ def display_header(use_resources: tuple[str, ...],
sanitizers.append("memory")
if ubsan:
sanitizers.append("undefined behavior")
+ if tsan:
+ sanitizers.append("thread")
if sanitizers:
print(f"== sanitizers: {', '.join(sanitizers)}")
for sanitizer, env_var in (
(asan, "ASAN_OPTIONS"),
(msan, "MSAN_OPTIONS"),
(ubsan, "UBSAN_OPTIONS"),
+ (tsan, "TSAN_OPTIONS"),
):
options= os.environ.get(env_var)
if sanitizer and options is not None:
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index cb5a84a..4e793f1 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -391,10 +391,10 @@ def skip_if_buildbot(reason=None):
isbuildbot = False
return unittest.skipIf(isbuildbot, reason)
-def check_sanitizer(*, address=False, memory=False, ub=False):
+def check_sanitizer(*, address=False, memory=False, ub=False, thread=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')
+ if not (address or memory or ub or thread):
+ raise ValueError('At least one of address, memory, ub or thread must be True')
cflags = sysconfig.get_config_var('CFLAGS') or ''
@@ -411,18 +411,23 @@ def check_sanitizer(*, address=False, memory=False, ub=False):
'-fsanitize=undefined' in cflags or
'--with-undefined-behavior-sanitizer' in config_args
)
+ thread_sanitizer = (
+ '-fsanitize=thread' in cflags or
+ '--with-thread-sanitizer' in config_args
+ )
return (
(memory and memory_sanitizer) or
(address and address_sanitizer) or
- (ub and ub_sanitizer)
+ (ub and ub_sanitizer) or
+ (thread and thread_sanitizer)
)
-def skip_if_sanitizer(reason=None, *, address=False, memory=False, ub=False):
+def skip_if_sanitizer(reason=None, *, address=False, memory=False, ub=False, thread=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)
+ skip = check_sanitizer(address=address, memory=memory, ub=ub, thread=thread)
return unittest.skipIf(skip, reason)
# gh-89363: True if fork() can hang if Python is built with Address Sanitizer
@@ -431,7 +436,7 @@ HAVE_ASAN_FORK_BUG = check_sanitizer(address=True)
def set_sanitizer_env_var(env, option):
- for name in ('ASAN_OPTIONS', 'MSAN_OPTIONS', 'UBSAN_OPTIONS'):
+ for name in ('ASAN_OPTIONS', 'MSAN_OPTIONS', 'UBSAN_OPTIONS', 'TSAN_OPTIONS'):
if name in env:
env[name] += f':{option}'
else:
diff --git a/Lib/test/test_io.py b/Lib/test/test_io.py
index 5e55624..daa40a6 100644
--- a/Lib/test/test_io.py
+++ b/Lib/test/test_io.py
@@ -1708,7 +1708,8 @@ class BufferedReaderTest(unittest.TestCase, CommonBufferedTests):
class CBufferedReaderTest(BufferedReaderTest, SizeofTest):
tp = io.BufferedReader
- @skip_if_sanitizer(memory=True, address=True, reason= "sanitizer defaults to crashing "
+ @skip_if_sanitizer(memory=True, address=True, thread=True,
+ reason="sanitizer defaults to crashing "
"instead of returning NULL for malloc failure.")
def test_constructor(self):
BufferedReaderTest.test_constructor(self)
@@ -2075,7 +2076,8 @@ class BufferedWriterTest(unittest.TestCase, CommonBufferedTests):
class CBufferedWriterTest(BufferedWriterTest, SizeofTest):
tp = io.BufferedWriter
- @skip_if_sanitizer(memory=True, address=True, reason= "sanitizer defaults to crashing "
+ @skip_if_sanitizer(memory=True, address=True, thread=True,
+ reason="sanitizer defaults to crashing "
"instead of returning NULL for malloc failure.")
def test_constructor(self):
BufferedWriterTest.test_constructor(self)
@@ -2596,7 +2598,8 @@ class BufferedRandomTest(BufferedReaderTest, BufferedWriterTest):
class CBufferedRandomTest(BufferedRandomTest, SizeofTest):
tp = io.BufferedRandom
- @skip_if_sanitizer(memory=True, address=True, reason= "sanitizer defaults to crashing "
+ @skip_if_sanitizer(memory=True, address=True, thread=True,
+ reason="sanitizer defaults to crashing "
"instead of returning NULL for malloc failure.")
def test_constructor(self):
BufferedRandomTest.test_constructor(self)
diff --git a/Misc/NEWS.d/next/Build/2023-12-17-18-23-02.gh-issue-112536.8lr3Ep.rst b/Misc/NEWS.d/next/Build/2023-12-17-18-23-02.gh-issue-112536.8lr3Ep.rst
new file mode 100644
index 0000000..a136eb4
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2023-12-17-18-23-02.gh-issue-112536.8lr3Ep.rst
@@ -0,0 +1 @@
+Add support for thread sanitizer (TSAN)
diff --git a/configure b/configure
index 938b6c6..be783fa 100755
--- a/configure
+++ b/configure
@@ -1089,6 +1089,7 @@ with_dsymutil
with_address_sanitizer
with_memory_sanitizer
with_undefined_behavior_sanitizer
+with_thread_sanitizer
with_hash_algorithm
with_tzpath
with_libs
@@ -1868,6 +1869,8 @@ Optional Packages:
--with-undefined-behavior-sanitizer
enable UndefinedBehaviorSanitizer undefined
behaviour detector, 'ubsan' (default is no)
+ --with-thread-sanitizer enable ThreadSanitizer data race detector, 'tsan'
+ (default is no)
--with-hash-algorithm=[fnv|siphash13|siphash24]
select hash algorithm for use in Python/pyhash.c
(default is SipHash13)
@@ -12661,6 +12664,28 @@ with_ubsan="no"
fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-thread-sanitizer" >&5
+printf %s "checking for --with-thread-sanitizer... " >&6; }
+
+# Check whether --with-thread_sanitizer was given.
+if test ${with_thread_sanitizer+y}
+then :
+ withval=$with_thread_sanitizer;
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $withval" >&5
+printf "%s\n" "$withval" >&6; }
+BASECFLAGS="-fsanitize=thread $BASECFLAGS"
+LDFLAGS="-fsanitize=thread $LDFLAGS"
+with_tsan="yes"
+
+else $as_nop
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+with_tsan="no"
+
+fi
+
+
# Set info about shared libraries.
diff --git a/configure.ac b/configure.ac
index b3eaaa9..8be26cc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3230,6 +3230,24 @@ AC_MSG_RESULT([no])
with_ubsan="no"
])
+AC_MSG_CHECKING([for --with-thread-sanitizer])
+AC_ARG_WITH(
+ [thread_sanitizer],
+ [AS_HELP_STRING(
+ [--with-thread-sanitizer],
+ [enable ThreadSanitizer data race detector, 'tsan' (default is no)]
+ )],
+[
+AC_MSG_RESULT([$withval])
+BASECFLAGS="-fsanitize=thread $BASECFLAGS"
+LDFLAGS="-fsanitize=thread $LDFLAGS"
+with_tsan="yes"
+],
+[
+AC_MSG_RESULT([no])
+with_tsan="no"
+])
+
# Set info about shared libraries.
AC_SUBST([SHLIB_SUFFIX])
AC_SUBST([LDSHARED])