From 76b7f048d5c270757e816fdeae1f37654da11c22 Mon Sep 17 00:00:00 2001 From: lipengyu Date: Sat, 12 Sep 2026 16:23:56 +0800 Subject: [PATCH 1/4] gh-157354 : Honor disabled sanitizer configure options --- ...-09-12-15-33-26.gh-issue-157354.2hw6fe.rst | 3 + configure | 86 ++++++++++++------- configure.ac | 38 ++++---- 3 files changed, 75 insertions(+), 52 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst diff --git a/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst b/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst new file mode 100644 index 00000000000000..906b70fcf5e5f3 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst @@ -0,0 +1,3 @@ +Honor ``=no`` and ``--without-...`` for the AddressSanitizer, +MemorySanitizer, UndefinedBehaviorSanitizer, and ThreadSanitizer configure +options without adding sanitizer flags or changing the pymalloc setting. diff --git a/configure b/configure index 1115be256afb49..43fbea67b317c2 100755 --- a/configure +++ b/configure @@ -14151,20 +14151,26 @@ printf %s "checking for --with-address-sanitizer... " >&6; } if test ${with_address_sanitizer+y} then : withval=$with_address_sanitizer; -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $withval" >&5 -printf "%s\n" "$withval" >&6; } +else case e in #( + e) with_address_sanitizer=no ;; +esac +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_address_sanitizer" >&5 +printf "%s\n" "$with_address_sanitizer" >&6; } +if test "x$with_address_sanitizer" = xno +then : + +else case e in #( + e) BASECFLAGS="-fsanitize=address -fno-omit-frame-pointer $BASECFLAGS" LDFLAGS="-fsanitize=address $LDFLAGS" # ASan 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; } @@ -14172,8 +14178,18 @@ printf %s "checking for --with-memory-sanitizer... " >&6; } if test ${with_memory_sanitizer+y} then : withval=$with_memory_sanitizer; -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $withval" >&5 -printf "%s\n" "$withval" >&6; } +else case e in #( + e) with_memory_sanitizer=no ;; +esac +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_memory_sanitizer" >&5 +printf "%s\n" "$with_memory_sanitizer" >&6; } +if test "x$with_memory_sanitizer" = xno +then : + +else case e in #( + e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fsanitize=memory" >&5 printf %s "checking whether C compiler accepts -fsanitize=memory... " >&6; } if test ${ax_cv_check_cflags___fsanitize_memory+y} @@ -14220,14 +14236,10 @@ fi # MSan 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-undefined-behavior-sanitizer" >&5 printf %s "checking for --with-undefined-behavior-sanitizer... " >&6; } @@ -14235,22 +14247,27 @@ printf %s "checking for --with-undefined-behavior-sanitizer... " >&6; } if test ${with_undefined_behavior_sanitizer+y} then : withval=$with_undefined_behavior_sanitizer; -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $withval" >&5 -printf "%s\n" "$withval" >&6; } -BASECFLAGS="-fsanitize=undefined $BASECFLAGS" -LDFLAGS="-fsanitize=undefined $LDFLAGS" -with_ubsan="yes" +else case e in #( + e) with_undefined_behavior_sanitizer=no ;; +esac +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_undefined_behavior_sanitizer" >&5 +printf "%s\n" "$with_undefined_behavior_sanitizer" >&6; } +if test "x$with_undefined_behavior_sanitizer" = xno +then : + +with_ubsan="no" else case e in #( e) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -with_ubsan="no" +BASECFLAGS="-fsanitize=undefined $BASECFLAGS" +LDFLAGS="-fsanitize=undefined $LDFLAGS" +with_ubsan="yes" ;; esac fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-thread-sanitizer" >&5 printf %s "checking for --with-thread-sanitizer... " >&6; } @@ -14258,22 +14275,27 @@ printf %s "checking for --with-thread-sanitizer... " >&6; } 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 case e in #( + e) with_thread_sanitizer=no ;; +esac +fi + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_thread_sanitizer" >&5 +printf "%s\n" "$with_thread_sanitizer" >&6; } +if test "x$with_thread_sanitizer" = xno +then : + +with_tsan="no" else case e in #( e) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -with_tsan="no" +BASECFLAGS="-fsanitize=thread $BASECFLAGS" +LDFLAGS="-fsanitize=thread $LDFLAGS" +with_tsan="yes" ;; esac fi - # Set info about shared libraries. diff --git a/configure.ac b/configure.ac index 902a822e43a42f..b937757294189d 100644 --- a/configure.ac +++ b/configure.ac @@ -3519,14 +3519,14 @@ AC_MSG_CHECKING([for --with-address-sanitizer]) AC_ARG_WITH([address_sanitizer], AS_HELP_STRING([--with-address-sanitizer], [enable AddressSanitizer memory error detector, 'asan' (default is no)]), -[ -AC_MSG_RESULT([$withval]) +[], [with_address_sanitizer=no]) +AC_MSG_RESULT([$with_address_sanitizer]) +AS_VAR_IF([with_address_sanitizer], [no], [], [ BASECFLAGS="-fsanitize=address -fno-omit-frame-pointer $BASECFLAGS" LDFLAGS="-fsanitize=address $LDFLAGS" # ASan 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( @@ -3535,16 +3535,16 @@ AC_ARG_WITH( [--with-memory-sanitizer], [enable MemorySanitizer allocation error detector, 'msan' (default is no)] )], -[ -AC_MSG_RESULT([$withval]) +[], [with_memory_sanitizer=no]) +AC_MSG_RESULT([$with_memory_sanitizer]) +AS_VAR_IF([with_memory_sanitizer], [no], [], [ AX_CHECK_COMPILE_FLAG([-fsanitize=memory],[ BASECFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer $BASECFLAGS" LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins=2 $LDFLAGS" ],[AC_MSG_ERROR([The selected compiler doesn't support memory sanitizer])]) # MSan works by controlling memory allocation, our own malloc interferes. with_pymalloc="no" -], -[AC_MSG_RESULT([no])]) +]) AC_MSG_CHECKING([for --with-undefined-behavior-sanitizer]) AC_ARG_WITH( @@ -3553,15 +3553,14 @@ AC_ARG_WITH( [--with-undefined-behavior-sanitizer], [enable UndefinedBehaviorSanitizer undefined behaviour detector, 'ubsan' (default is no)] )], -[ -AC_MSG_RESULT([$withval]) +[], [with_undefined_behavior_sanitizer=no]) +AC_MSG_RESULT([$with_undefined_behavior_sanitizer]) +AS_VAR_IF([with_undefined_behavior_sanitizer], [no], [ +with_ubsan="no" +], [ BASECFLAGS="-fsanitize=undefined $BASECFLAGS" LDFLAGS="-fsanitize=undefined $LDFLAGS" with_ubsan="yes" -], -[ -AC_MSG_RESULT([no]) -with_ubsan="no" ]) AC_MSG_CHECKING([for --with-thread-sanitizer]) @@ -3571,15 +3570,14 @@ AC_ARG_WITH( [--with-thread-sanitizer], [enable ThreadSanitizer data race detector, 'tsan' (default is no)] )], -[ -AC_MSG_RESULT([$withval]) +[], [with_thread_sanitizer=no]) +AC_MSG_RESULT([$with_thread_sanitizer]) +AS_VAR_IF([with_thread_sanitizer], [no], [ +with_tsan="no" +], [ BASECFLAGS="-fsanitize=thread $BASECFLAGS" LDFLAGS="-fsanitize=thread $LDFLAGS" with_tsan="yes" -], -[ -AC_MSG_RESULT([no]) -with_tsan="no" ]) # Set info about shared libraries. From cc804f8a33ac7e00990d5dcd760a34c4e00d51f5 Mon Sep 17 00:00:00 2001 From: lipengyu Date: Sat, 24 Oct 2026 09:29:07 +0800 Subject: [PATCH 2/4] gh-157354: Recognize disabled sanitizer configure options in tests --- Lib/test/support/__init__.py | 25 ++++++++++++++----- Lib/test/test_support.py | 24 ++++++++++++++++++ ...-09-12-15-33-26.gh-issue-157354.2hw6fe.rst | 3 +-- configure | 10 -------- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 4f27a23b1a62b1..3744d6279e7b46 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -11,6 +11,7 @@ import _opcode import os import re +import shlex import stat import sys import sysconfig @@ -469,27 +470,39 @@ def check_sanitizer(*, address=False, hwaddress=False, memory=False, ub=False, cflags = sysconfig.get_config_var('CFLAGS') or '' - config_args = sysconfig.get_config_var('CONFIG_ARGS') or '' + config_args = shlex.split(sysconfig.get_config_var('CONFIG_ARGS') or '') + + def configured_with_sanitizer(name): + with_option = f'--with-{name}-sanitizer' + without_option = f'--without-{name}-sanitizer' + enabled = False + for arg in config_args: + if arg == without_option or arg == f'{with_option}=no': + enabled = False + elif arg == with_option or arg.startswith(f'{with_option}='): + enabled = True + return enabled + memory_sanitizer = ( '-fsanitize=memory' in cflags or - '--with-memory-sanitizer' in config_args + configured_with_sanitizer('memory') ) hwaddress_sanitizer = ( '-fsanitize=hwaddress' in cflags or - '--with-hwaddress-sanitizer' in config_args + configured_with_sanitizer('hwaddress') ) address_sanitizer = ( '-fsanitize=address' in cflags or - '--with-address-sanitizer' in config_args or + configured_with_sanitizer('address') or hwaddress_sanitizer ) ub_sanitizer = ( '-fsanitize=undefined' in cflags or - '--with-undefined-behavior-sanitizer' in config_args + configured_with_sanitizer('undefined-behavior') ) thread_sanitizer = ( '-fsanitize=thread' in cflags or - '--with-thread-sanitizer' in config_args + configured_with_sanitizer('thread') ) function_sanitizer = ( '-fsanitize=function' in cflags diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index b83e0c371db33d..d35299b94ff69c 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -17,6 +17,7 @@ import textwrap import unittest import warnings +from unittest import mock from test import support from test.support import isolation @@ -60,6 +61,29 @@ def _caplog(): class TestSupport(unittest.TestCase): + def test_check_sanitizer_config_args(self): + sanitizers = ( + ('address', 'address'), + ('hwaddress', 'hwaddress'), + ('memory', 'memory'), + ('undefined-behavior', 'ub'), + ('thread', 'thread'), + ) + for option, keyword in sanitizers: + for config_arg, expected in ( + (f'--with-{option}-sanitizer=no', False), + (f'--without-{option}-sanitizer', False), + (f'--with-{option}-sanitizer', True), + (f'--with-{option}-sanitizer=yes', True), + ): + with self.subTest(config_arg=config_arg): + config = {'CFLAGS': '', 'CONFIG_ARGS': f"'{config_arg}'"} + with mock.patch.object(sysconfig, 'get_config_var', + side_effect=config.get): + self.assertEqual( + support.check_sanitizer(**{keyword: True}), expected + ) + @classmethod def setUpClass(cls): orig_filter_len = len(warnings._get_filters()) diff --git a/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst b/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst index 0012bd86a16304..ac6e28caf08dca 100644 --- a/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst +++ b/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst @@ -1,4 +1,3 @@ Honor ``=no`` and ``--without-...`` for the AddressSanitizer, HWAddressSanitizer, MemorySanitizer, UndefinedBehaviorSanitizer, and -ThreadSanitizer configure options without adding sanitizer flags or changing -the pymalloc setting. +ThreadSanitizer configure options without adding sanitizer flags. diff --git a/configure b/configure index febc1c7e6d12f7..dd89018c35cf50 100755 --- a/configure +++ b/configure @@ -14451,12 +14451,10 @@ esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - ;; esac fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-hwaddress-sanitizer" >&5 printf %s "checking for --with-hwaddress-sanitizer... " >&6; } @@ -14514,12 +14512,10 @@ esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - ;; esac fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-memory-sanitizer" >&5 printf %s "checking for --with-memory-sanitizer... " >&6; } @@ -14566,12 +14562,10 @@ esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - ;; esac fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-undefined-behavior-sanitizer" >&5 printf %s "checking for --with-undefined-behavior-sanitizer... " >&6; } @@ -14621,12 +14615,10 @@ esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - ;; esac fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for --with-thread-sanitizer" >&5 printf %s "checking for --with-thread-sanitizer... " >&6; } @@ -14676,12 +14668,10 @@ esac fi rm -f core conftest.err conftest.$ac_objext conftest.beam \ conftest$ac_exeext conftest.$ac_ext - ;; esac fi - # Set info about shared libraries. From 80ab094f43d7fdc36129ee5ea5ac35250ea12903 Mon Sep 17 00:00:00 2001 From: lipengyu Date: Sun, 25 Oct 2026 08:08:06 +0800 Subject: [PATCH 3/4] gh-157354: Align sanitizer test placement and NEWS wording --- Lib/test/test_support.py | 40 +++++++++---------- ...-09-12-15-33-26.gh-issue-157354.2hw6fe.rst | 5 +-- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index d35299b94ff69c..938eef1038f928 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -61,6 +61,26 @@ def _caplog(): class TestSupport(unittest.TestCase): + @classmethod + def setUpClass(cls): + orig_filter_len = len(warnings._get_filters()) + cls._warnings_helper_token = support.ignore_deprecations_from( + "test.support.warnings_helper", like=".*used in test_support.*" + ) + cls._test_support_token = support.ignore_deprecations_from( + __name__, like=".*You should NOT be seeing this.*" + ) + assert len(warnings._get_filters()) == orig_filter_len + 2 + + @classmethod + def tearDownClass(cls): + orig_filter_len = len(warnings._get_filters()) + support.clear_ignored_deprecations( + cls._warnings_helper_token, + cls._test_support_token, + ) + assert len(warnings._get_filters()) == orig_filter_len - 2 + def test_check_sanitizer_config_args(self): sanitizers = ( ('address', 'address'), @@ -84,26 +104,6 @@ def test_check_sanitizer_config_args(self): support.check_sanitizer(**{keyword: True}), expected ) - @classmethod - def setUpClass(cls): - orig_filter_len = len(warnings._get_filters()) - cls._warnings_helper_token = support.ignore_deprecations_from( - "test.support.warnings_helper", like=".*used in test_support.*" - ) - cls._test_support_token = support.ignore_deprecations_from( - __name__, like=".*You should NOT be seeing this.*" - ) - assert len(warnings._get_filters()) == orig_filter_len + 2 - - @classmethod - def tearDownClass(cls): - orig_filter_len = len(warnings._get_filters()) - support.clear_ignored_deprecations( - cls._warnings_helper_token, - cls._test_support_token, - ) - assert len(warnings._get_filters()) == orig_filter_len - 2 - def test_ignored_deprecations_are_silent(self): """Test support.ignore_deprecations_from() silences warnings""" with warnings.catch_warnings(record=True) as warning_objs: diff --git a/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst b/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst index ac6e28caf08dca..5bc35aaf9870c2 100644 --- a/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst +++ b/Misc/NEWS.d/next/Build/2026-09-12-15-33-26.gh-issue-157354.2hw6fe.rst @@ -1,3 +1,2 @@ -Honor ``=no`` and ``--without-...`` for the AddressSanitizer, -HWAddressSanitizer, MemorySanitizer, UndefinedBehaviorSanitizer, and -ThreadSanitizer configure options without adding sanitizer flags. +Fix the :samp:`--with-{name}-sanitizer=no` and :samp:`--without-{name}-sanitizer` +:program:`configure` options enabling the sanitizer instead of disabling it. From 983ef0bd5d54cb57d3552ad75336197b34f7f2b6 Mon Sep 17 00:00:00 2001 From: lipengyu Date: Sun, 25 Oct 2026 08:14:16 +0800 Subject: [PATCH 4/4] remove test_check_sanitizer_config_args --- Lib/test/test_support.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 938eef1038f928..b83e0c371db33d 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -17,7 +17,6 @@ import textwrap import unittest import warnings -from unittest import mock from test import support from test.support import isolation @@ -81,29 +80,6 @@ def tearDownClass(cls): ) assert len(warnings._get_filters()) == orig_filter_len - 2 - def test_check_sanitizer_config_args(self): - sanitizers = ( - ('address', 'address'), - ('hwaddress', 'hwaddress'), - ('memory', 'memory'), - ('undefined-behavior', 'ub'), - ('thread', 'thread'), - ) - for option, keyword in sanitizers: - for config_arg, expected in ( - (f'--with-{option}-sanitizer=no', False), - (f'--without-{option}-sanitizer', False), - (f'--with-{option}-sanitizer', True), - (f'--with-{option}-sanitizer=yes', True), - ): - with self.subTest(config_arg=config_arg): - config = {'CFLAGS': '', 'CONFIG_ARGS': f"'{config_arg}'"} - with mock.patch.object(sysconfig, 'get_config_var', - side_effect=config.get): - self.assertEqual( - support.check_sanitizer(**{keyword: True}), expected - ) - def test_ignored_deprecations_are_silent(self): """Test support.ignore_deprecations_from() silences warnings""" with warnings.catch_warnings(record=True) as warning_objs: