Skip to content

Commit

Permalink
backport thread sanitizer to 3.10
Browse files Browse the repository at this point in the history
  • Loading branch information
comicfans committed Sep 8, 2024
1 parent 0c5fc27 commit f5edd34
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 9 deletions.
7 changes: 7 additions & 0 deletions Doc/using/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,13 @@ Debug options

.. versionadded:: 3.6

.. option:: --with-thread-sanitizer

Enable ThreadSanitizer data race detector, ``tsan``
(default is no).

.. versionadded:: 3.13


Linker options
--------------
Expand Down
8 changes: 8 additions & 0 deletions Include/Python.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,18 @@
# 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
# endif
# if defined(__SANITIZE_THREAD__)
# define _Py_THREAD_SANITIZER
# endif
#endif

#include "pymath.h"
Expand Down
17 changes: 11 additions & 6 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,10 +367,10 @@ def wrapper(*args, **kw):
return decorator


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 ''
Expand All @@ -387,18 +387,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)


Expand Down
9 changes: 6 additions & 3 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,8 @@ def test_truncate_on_read_only(self):
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)
Expand Down Expand Up @@ -1912,7 +1913,8 @@ def test_slow_close_from_thread(self):
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)
Expand Down Expand Up @@ -2411,7 +2413,8 @@ def test_interleaved_readline_write(self):
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)
Expand Down
25 changes: 25 additions & 0 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@ with_lto
with_address_sanitizer
with_memory_sanitizer
with_undefined_behavior_sanitizer
with_thread_sanitizer
with_hash_algorithm
with_tzpath
with_libs
Expand Down Expand Up @@ -1560,6 +1561,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|siphash24]
select hash algorithm for use in Python/pyhash.c
(default is SipHash24)
Expand Down Expand Up @@ -9684,6 +9687,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.


Expand Down
18 changes: 18 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2612,6 +2612,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)
Expand Down

0 comments on commit f5edd34

Please sign in to comment.