Skip to content

Commit 197607e

Browse files
StanFromIrelandfmayerAnnaAr321vstinner
authored
gh-156049: Add support for building with HWAsan (#156721)
Co-authored-by: Florian Mayer <fmayer@google.com> Co-authored-by: Anna <araslanova.anna.a@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent ea0ee92 commit 197607e

9 files changed

Lines changed: 168 additions & 6 deletions

File tree

‎Doc/using/configure.rst‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,19 @@ Debug options
10221022

10231023
.. versionadded:: 3.6
10241024

1025+
.. option:: --with-hwaddress-sanitizer
1026+
1027+
Enable HWAddressSanitizer memory error detector, ``hwasan`` (default is no).
1028+
Note that on x86-64 this uses `page aliasing
1029+
<https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#supported-architectures>`_,
1030+
which only tags heap allocations and is unsafe for programs that ``fork()``,
1031+
including much of the test suite.
1032+
See the `LLVM HWASan design documentation
1033+
<https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html>`_
1034+
for more information.
1035+
1036+
.. versionadded:: next
1037+
10251038
.. option:: --with-memory-sanitizer
10261039

10271040
Enable MemorySanitizer allocation error detector, ``msan`` (default is no).

‎Doc/whatsnew/3.16.rst‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,11 @@ Build changes
11711171

11721172
(Contributed by Stan Ulbrych in :gh:`139314`.)
11731173

1174+
* Add the :option:`--with-hwaddress-sanitizer` :program:`configure` option to
1175+
build with `HWAddressSanitizer <https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html>`_.
1176+
1177+
(Contributed by Stan Ulbrych, Florian Mayer and AnnaAr321 in :gh:`156049`.)
1178+
11741179

11751180
C API changes
11761181
=============

‎Include/pyport.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,12 @@ extern "C" {
567567
# define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
568568
# endif
569569
# endif
570+
# if __has_feature(hwaddress_sanitizer)
571+
# if !defined(_Py_ADDRESS_SANITIZER)
572+
# define _Py_ADDRESS_SANITIZER
573+
# define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("hwaddress")))
574+
# endif
575+
# endif
570576
# if __has_feature(thread_sanitizer)
571577
# if !defined(_Py_THREAD_SANITIZER)
572578
# define _Py_THREAD_SANITIZER
@@ -577,6 +583,9 @@ extern "C" {
577583
# if defined(__SANITIZE_ADDRESS__)
578584
# define _Py_ADDRESS_SANITIZER
579585
# define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
586+
# elif defined(__SANITIZE_HWADDRESS__)
587+
# define _Py_ADDRESS_SANITIZER
588+
# define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("hwaddress")))
580589
# endif
581590
# if defined(__SANITIZE_THREAD__)
582591
# define _Py_THREAD_SANITIZER

‎Lib/test/libregrtest/utils.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,9 @@ def get_build_info():
397397

398398
# --with-address-sanitizer
399399
sanitizers = []
400-
if support.check_sanitizer(address=True):
400+
if support.check_sanitizer(hwaddress=True):
401+
sanitizers.append("HWASAN")
402+
elif support.check_sanitizer(address=True):
401403
sanitizers.append("ASAN")
402404
# --with-memory-sanitizer
403405
if support.check_sanitizer(memory=True):

‎Lib/test/support/__init__.py‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -448,11 +448,12 @@ def skip_if_buildbot(reason=None):
448448
isbuildbot = False
449449
return unittest.skipIf(isbuildbot, reason)
450450

451-
def check_sanitizer(*, address=False, memory=False, ub=False, thread=False,
452-
function=True):
451+
def check_sanitizer(*, address=False, hwaddress=False, memory=False, ub=False,
452+
thread=False, function=True):
453453
"""Returns True if Python is compiled with sanitizer support"""
454-
if not (address or memory or ub or thread):
455-
raise ValueError('At least one of address, memory, ub or thread must be True')
454+
if not (address or hwaddress or memory or ub or thread):
455+
raise ValueError('At least one of address, hwaddress, memory, ub or '
456+
'thread must be True')
456457

457458

458459
cflags = sysconfig.get_config_var('CFLAGS') or ''
@@ -461,9 +462,14 @@ def check_sanitizer(*, address=False, memory=False, ub=False, thread=False,
461462
'-fsanitize=memory' in cflags or
462463
'--with-memory-sanitizer' in config_args
463464
)
465+
hwaddress_sanitizer = (
466+
'-fsanitize=hwaddress' in cflags or
467+
'--with-hwaddress-sanitizer' in config_args
468+
)
464469
address_sanitizer = (
465470
'-fsanitize=address' in cflags or
466-
'--with-address-sanitizer' in config_args
471+
'--with-address-sanitizer' in config_args or
472+
hwaddress_sanitizer
467473
)
468474
ub_sanitizer = (
469475
'-fsanitize=undefined' in cflags or
@@ -479,6 +485,7 @@ def check_sanitizer(*, address=False, memory=False, ub=False, thread=False,
479485
return (
480486
(memory and memory_sanitizer) or
481487
(address and address_sanitizer) or
488+
(hwaddress and hwaddress_sanitizer) or
482489
(ub and ub_sanitizer) or
483490
(thread and thread_sanitizer) or
484491
(function and function_sanitizer)

‎Lib/test/test_capi/test_mem.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,5 +182,18 @@ class PyMemDefaultTests(PyMemDebugTests):
182182
PYTHONMALLOC = ''
183183

184184

185+
@requires_subprocess()
186+
@unittest.skipUnless(support.check_sanitizer(address=True),
187+
'need address sanitizer')
188+
class AddressSanitizerTests(unittest.TestCase):
189+
def test_buffer_overflow(self):
190+
with support.SuppressCrashReport():
191+
out = assert_python_failure(
192+
'-c', 'import _testcapi; _testcapi.pymem_buffer_overflow()',
193+
PYTHONMALLOC='malloc',
194+
)
195+
self.assertIn(b'AddressSanitizer', out.err)
196+
197+
185198
if __name__ == "__main__":
186199
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :option:`--with-hwaddress-sanitizer` to build with `HWAddressSanitizer
2+
<https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html>`_.

‎configure‎

Lines changed: 83 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎configure.ac‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3531,6 +3531,34 @@ with_pymalloc="no"
35313531
],
35323532
[AC_MSG_RESULT([no])])
35333533

3534+
AC_MSG_CHECKING([for --with-hwaddress-sanitizer])
3535+
AC_ARG_WITH(
3536+
[hwaddress_sanitizer],
3537+
[AS_HELP_STRING(
3538+
[--with-hwaddress-sanitizer],
3539+
[enable HWAddressSanitizer memory error detector, 'hwasan' (default is no)]
3540+
)],
3541+
[
3542+
AC_MSG_RESULT([$withval])
3543+
hwasan_flags="-fsanitize=hwaddress"
3544+
# x86-64 lacks address tagging, so HWASan needs the page aliasing mode there.
3545+
# See gh-156049 and
3546+
# https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#supported-architectures
3547+
AS_CASE([$host_cpu],
3548+
[x86_64|amd64], [hwasan_flags="$hwasan_flags -fsanitize-hwaddress-experimental-aliasing"]
3549+
)
3550+
AX_CHECK_COMPILE_FLAG([$hwasan_flags],[
3551+
BASECFLAGS="$hwasan_flags -fno-omit-frame-pointer $BASECFLAGS"
3552+
LDFLAGS="$hwasan_flags $LDFLAGS"
3553+
],[AC_MSG_ERROR([The selected compiler doesn't support hardware address sanitizer])])
3554+
# HWASan works by controlling memory allocation, our own malloc interferes,
3555+
# so disable it by default, but allow --with-pymalloc to override.
3556+
if test -z "$with_pymalloc"; then
3557+
with_pymalloc="no"
3558+
fi
3559+
],
3560+
[AC_MSG_RESULT([no])])
3561+
35343562
AC_MSG_CHECKING([for --with-memory-sanitizer])
35353563
AC_ARG_WITH(
35363564
[memory_sanitizer],

0 commit comments

Comments
 (0)