diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst index 8b4940ceb9521a7..11dd73633465c0c 100644 --- a/Doc/using/configure.rst +++ b/Doc/using/configure.rst @@ -1015,6 +1015,16 @@ 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 + `_, + which only tags heap allocations and is unsafe for programs that ``fork()``, + including much of the test suite. + + .. versionadded:: 3.16 + .. 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 4c432fb249f2462..b0cbf2fc72b9f7a 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -934,6 +934,11 @@ Build changes (Contributed by Stan Ulbrych in :gh:`139314`.) +* Add the :option:`--with-hwaddress-sanitizer` :program:`configure` option to + build with `HWAddressSanitizer `_. + + (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 73a3e6cdaf09200..1592acb24764ab5 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -562,6 +562,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 @@ -572,6 +578,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/support/__init__.py b/Lib/test/support/__init__.py index be71575a6ea06a9..bf2aa87dfb914a2 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -463,7 +463,9 @@ def check_sanitizer(*, address=False, memory=False, ub=False, thread=False, ) address_sanitizer = ( '-fsanitize=address' in cflags or - '--with-address-sanitizer' in config_args + '-fsanitize=hwaddress' in cflags or + '--with-address-sanitizer' in config_args or + '--with-hwaddress-sanitizer' in config_args ) ub_sanitizer = ( '-fsanitize=undefined' in cflags or 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 000000000000000..622d282f9e88730 --- /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 +`_. diff --git a/configure b/configure index 6b560fe6841b722..609a084d8aff0cc 100755 --- a/configure +++ b/configure @@ -1123,6 +1123,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 @@ -1931,6 +1932,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 @@ -14170,6 +14174,81 @@ 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 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. +with_pymalloc="no" + +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 476f13c82bbb2b9..7b3a0dfd5673a1d 100644 --- a/configure.ac +++ b/configure.ac @@ -3533,6 +3533,30 @@ 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 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. +with_pymalloc="no" +], +[AC_MSG_RESULT([no])]) + AC_MSG_CHECKING([for --with-memory-sanitizer]) AC_ARG_WITH( [memory_sanitizer],