https://github.com/python/cpython/commit/197607e8656a3c5c7a3c77926b52db409c8bae7d
commit: 197607e8656a3c5c7a3c77926b52db409c8bae7d
branch: main
author: Stan Ulbrych <[email protected]>
committer: StanFromIreland <[email protected]>
date: 2026-10-06T19:34:53+01:00
summary:

gh-156049: Add support for building with HWAsan (#156721)

Co-authored-by: Florian Mayer <[email protected]>
Co-authored-by: Anna <[email protected]>
Co-authored-by: Victor Stinner <[email protected]>

files:
A Misc/NEWS.d/next/Build/2026-08-31-15-50-04.gh-issue-156049.FCf6hP.rst
M Doc/using/configure.rst
M Doc/whatsnew/3.16.rst
M Include/pyport.h
M Lib/test/libregrtest/utils.py
M Lib/test/support/__init__.py
M Lib/test/test_capi/test_mem.py
M configure
M configure.ac

diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst
index d6f21712b103ba..ab4ec42a575369 100644
--- a/Doc/using/configure.rst
+++ b/Doc/using/configure.rst
@@ -1022,6 +1022,19 @@ Debug options
 
    .. versionadded:: 3.6
 
+.. option:: --with-hwaddress-sanitizer
+
+   Enable HWAddressSanitizer memory error detector, ``hwasan`` (default is no).
+   Note that on x86-64 this uses `page aliasing
+   
<https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#supported-architectures>`_,
+   which only tags heap allocations and is unsafe for programs that ``fork()``,
+   including much of the test suite.
+   See the `LLVM HWASan design documentation
+   <https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html>`_
+   for more information.
+
+   .. versionadded:: next
+
 .. option:: --with-memory-sanitizer
 
    Enable MemorySanitizer allocation error detector, ``msan`` (default is no).
diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst
index 65db9bd6bf70ba..524252cab87936 100644
--- a/Doc/whatsnew/3.16.rst
+++ b/Doc/whatsnew/3.16.rst
@@ -1171,6 +1171,11 @@ Build changes
 
   (Contributed by Stan Ulbrych in :gh:`139314`.)
 
+* Add the :option:`--with-hwaddress-sanitizer` :program:`configure` option to
+  build with `HWAddressSanitizer 
<https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html>`_.
+
+  (Contributed by Stan Ulbrych, Florian Mayer and AnnaAr321 in :gh:`156049`.)
+
 
 C API changes
 =============
diff --git a/Include/pyport.h b/Include/pyport.h
index 2206beaa77221c..1af2b61eeacd28 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -567,6 +567,12 @@ extern "C" {
 #      define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
 #    endif
 #  endif
+#  if __has_feature(hwaddress_sanitizer)
+#    if !defined(_Py_ADDRESS_SANITIZER)
+#      define _Py_ADDRESS_SANITIZER
+#      define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("hwaddress")))
+#    endif
+#  endif
 #  if __has_feature(thread_sanitizer)
 #    if !defined(_Py_THREAD_SANITIZER)
 #      define _Py_THREAD_SANITIZER
@@ -577,6 +583,9 @@ extern "C" {
 #  if defined(__SANITIZE_ADDRESS__)
 #    define _Py_ADDRESS_SANITIZER
 #    define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
+#  elif defined(__SANITIZE_HWADDRESS__)
+#    define _Py_ADDRESS_SANITIZER
+#    define _Py_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("hwaddress")))
 #  endif
 #  if defined(__SANITIZE_THREAD__)
 #    define _Py_THREAD_SANITIZER
diff --git a/Lib/test/libregrtest/utils.py b/Lib/test/libregrtest/utils.py
index 83e0575619c99a..b34f106a83ca85 100644
--- a/Lib/test/libregrtest/utils.py
+++ b/Lib/test/libregrtest/utils.py
@@ -397,7 +397,9 @@ def get_build_info():
 
     # --with-address-sanitizer
     sanitizers = []
-    if support.check_sanitizer(address=True):
+    if support.check_sanitizer(hwaddress=True):
+        sanitizers.append("HWASAN")
+    elif support.check_sanitizer(address=True):
         sanitizers.append("ASAN")
     # --with-memory-sanitizer
     if support.check_sanitizer(memory=True):
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index e9fc4cca229a03..5331615b1b1587 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -448,11 +448,12 @@ def skip_if_buildbot(reason=None):
         isbuildbot = False
     return unittest.skipIf(isbuildbot, reason)
 
-def check_sanitizer(*, address=False, memory=False, ub=False, thread=False,
-                    function=True):
+def check_sanitizer(*, address=False, hwaddress=False, memory=False, ub=False,
+                    thread=False, function=True):
     """Returns True if Python is compiled with sanitizer support"""
-    if not (address or memory or ub or thread):
-        raise ValueError('At least one of address, memory, ub or thread must 
be True')
+    if not (address or hwaddress or memory or ub or thread):
+        raise ValueError('At least one of address, hwaddress, memory, ub or '
+                         'thread must be True')
 
 
     cflags = sysconfig.get_config_var('CFLAGS') or ''
@@ -461,9 +462,14 @@ def check_sanitizer(*, address=False, memory=False, 
ub=False, thread=False,
         '-fsanitize=memory' in cflags or
         '--with-memory-sanitizer' in config_args
     )
+    hwaddress_sanitizer = (
+        '-fsanitize=hwaddress' in cflags or
+        '--with-hwaddress-sanitizer' in config_args
+    )
     address_sanitizer = (
         '-fsanitize=address' in cflags or
-        '--with-address-sanitizer' in config_args
+        '--with-address-sanitizer' in config_args or
+        hwaddress_sanitizer
     )
     ub_sanitizer = (
         '-fsanitize=undefined' in cflags or
@@ -479,6 +485,7 @@ def check_sanitizer(*, address=False, memory=False, 
ub=False, thread=False,
     return (
         (memory and memory_sanitizer) or
         (address and address_sanitizer) or
+        (hwaddress and hwaddress_sanitizer) or
         (ub and ub_sanitizer) or
         (thread and thread_sanitizer) or
         (function and function_sanitizer)
diff --git a/Lib/test/test_capi/test_mem.py b/Lib/test/test_capi/test_mem.py
index e6389b4e0c1e92..63e71501162452 100644
--- a/Lib/test/test_capi/test_mem.py
+++ b/Lib/test/test_capi/test_mem.py
@@ -182,5 +182,18 @@ class PyMemDefaultTests(PyMemDebugTests):
     PYTHONMALLOC = ''
 
 
+@requires_subprocess()
[email protected](support.check_sanitizer(address=True),
+                     'need address sanitizer')
+class AddressSanitizerTests(unittest.TestCase):
+    def test_buffer_overflow(self):
+        with support.SuppressCrashReport():
+            out = assert_python_failure(
+                '-c', 'import _testcapi; _testcapi.pymem_buffer_overflow()',
+                PYTHONMALLOC='malloc',
+            )
+        self.assertIn(b'AddressSanitizer', out.err)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git 
a/Misc/NEWS.d/next/Build/2026-08-31-15-50-04.gh-issue-156049.FCf6hP.rst 
b/Misc/NEWS.d/next/Build/2026-08-31-15-50-04.gh-issue-156049.FCf6hP.rst
new file mode 100644
index 00000000000000..622d282f9e8873
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2026-08-31-15-50-04.gh-issue-156049.FCf6hP.rst
@@ -0,0 +1,2 @@
+Add :option:`--with-hwaddress-sanitizer` to build with `HWAddressSanitizer
+<https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html>`_.
diff --git a/configure b/configure
index e5f33f0f4e5ac9..deb01864e73178 100755
--- a/configure
+++ b/configure
@@ -1122,6 +1122,7 @@ with_frame_pointers
 enable_experimental_jit
 with_dsymutil
 with_address_sanitizer
+with_hwaddress_sanitizer
 with_memory_sanitizer
 with_undefined_behavior_sanitizer
 with_thread_sanitizer
@@ -1930,6 +1931,9 @@ Optional Packages:
   --with-address-sanitizer
                           enable AddressSanitizer memory error detector,
                           'asan' (default is no)
+  --with-hwaddress-sanitizer
+                          enable HWAddressSanitizer memory error detector,
+                          'hwasan' (default is no)
   --with-memory-sanitizer enable MemorySanitizer allocation error detector,
                           'msan' (default is no)
   --with-undefined-behavior-sanitizer
@@ -14157,6 +14161,85 @@ esac
 fi
 
 
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for 
--with-hwaddress-sanitizer" >&5
+printf %s "checking for --with-hwaddress-sanitizer... " >&6; }
+
+# Check whether --with-hwaddress_sanitizer was given.
+if test ${with_hwaddress_sanitizer+y}
+then :
+  withval=$with_hwaddress_sanitizer;
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $withval" >&5
+printf "%s\n" "$withval" >&6; }
+hwasan_flags="-fsanitize=hwaddress"
+# x86-64 lacks address tagging, so HWASan needs the page aliasing mode there.
+# See gh-156049 and
+# 
https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#supported-architectures
+case $host_cpu in #(
+  x86_64|amd64) :
+    hwasan_flags="$hwasan_flags -fsanitize-hwaddress-experimental-aliasing"
+ ;; #(
+  *) :
+     ;;
+esac
+as_CACHEVAR=`printf "%s\n" "ax_cv_check_cflags__$hwasan_flags" | sed 
"$as_sed_sh"`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler 
accepts $hwasan_flags" >&5
+printf %s "checking whether C compiler accepts $hwasan_flags... " >&6; }
+if eval test \${$as_CACHEVAR+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e)
+  ax_check_save_flags=$CFLAGS
+  CFLAGS="$CFLAGS  $hwasan_flags"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$as_CACHEVAR=yes"
+else case e in #(
+  e) eval "$as_CACHEVAR=no" ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  CFLAGS=$ax_check_save_flags ;;
+esac
+fi
+eval ac_res=\$$as_CACHEVAR
+              { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" 
>&5
+printf "%s\n" "$ac_res" >&6; }
+if eval test \"x\$"$as_CACHEVAR"\" = x"yes"
+then :
+
+BASECFLAGS="$hwasan_flags -fno-omit-frame-pointer $BASECFLAGS"
+LDFLAGS="$hwasan_flags $LDFLAGS"
+
+else case e in #(
+  e) as_fn_error $? "The selected compiler doesn't support hardware address 
sanitizer" "$LINENO" 5 ;;
+esac
+fi
+
+# HWASan works by controlling memory allocation, our own malloc interferes,
+# so disable it by default, but allow --with-pymalloc to override.
+if test -z "$with_pymalloc"; then
+  with_pymalloc="no"
+fi
+
+else case e in #(
+  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; } ;;
+esac
+fi
+
+
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for 
--with-memory-sanitizer" >&5
 printf %s "checking for --with-memory-sanitizer... " >&6; }
 
diff --git a/configure.ac b/configure.ac
index 86227335731371..a1802f303e95eb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3531,6 +3531,34 @@ with_pymalloc="no"
 ],
 [AC_MSG_RESULT([no])])
 
+AC_MSG_CHECKING([for --with-hwaddress-sanitizer])
+AC_ARG_WITH(
+  [hwaddress_sanitizer],
+  [AS_HELP_STRING(
+    [--with-hwaddress-sanitizer],
+    [enable HWAddressSanitizer memory error detector, 'hwasan' (default is no)]
+  )],
+[
+AC_MSG_RESULT([$withval])
+hwasan_flags="-fsanitize=hwaddress"
+# x86-64 lacks address tagging, so HWASan needs the page aliasing mode there.
+# See gh-156049 and
+# 
https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html#supported-architectures
+AS_CASE([$host_cpu],
+  [x86_64|amd64], [hwasan_flags="$hwasan_flags 
-fsanitize-hwaddress-experimental-aliasing"]
+)
+AX_CHECK_COMPILE_FLAG([$hwasan_flags],[
+BASECFLAGS="$hwasan_flags -fno-omit-frame-pointer $BASECFLAGS"
+LDFLAGS="$hwasan_flags $LDFLAGS"
+],[AC_MSG_ERROR([The selected compiler doesn't support hardware address 
sanitizer])])
+# HWASan works by controlling memory allocation, our own malloc interferes,
+# so disable it by default, but allow --with-pymalloc to override.
+if test -z "$with_pymalloc"; then
+  with_pymalloc="no"
+fi
+],
+[AC_MSG_RESULT([no])])
+
 AC_MSG_CHECKING([for --with-memory-sanitizer])
 AC_ARG_WITH(
   [memory_sanitizer],

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to