https://github.com/python/cpython/commit/2791c35cae1fa274198bb2d3b3dccb378dfe40f3
commit: 2791c35cae1fa274198bb2d3b3dccb378dfe40f3
branch: main
author: Vasiliy Kiryanov <[email protected]>
committer: encukou <[email protected]>
date: 2026-10-07T18:05:37+02:00
summary:

gh-136872: Build pymalloc in sanitizer builds, but disable it by default 
(GH-157934)

--with-*address-sanitizer and --with-memory-sanitizer unconditionally
disabled pymalloc (even when --with-pymalloc was given explicitly).

Enable it, so that it's available with `PYTHONMALLOC=pymalloc`.
Disable it by default on the affected sanitizer builds.

files:
A Misc/NEWS.d/next/Build/2026-09-21-19-20-42.gh-issue-136872.a2tSc_.rst
M Doc/c-api/memory.rst
M Doc/using/configure.rst
M Include/internal/pycore_pymem_init.h
M Lib/test/test_cmd_line.py
M Lib/test/test_sys.py
M Objects/obmalloc.c
M configure
M configure.ac

diff --git a/Doc/c-api/memory.rst b/Doc/c-api/memory.rst
index 73310670ac371c..94be7dde89dbf3 100644
--- a/Doc/c-api/memory.rst
+++ b/Doc/c-api/memory.rst
@@ -437,6 +437,8 @@ Release build                        ``"pymalloc"``         
  ``malloc``
 Debug build                          ``"pymalloc_debug"``     ``malloc`` + 
debug    ``pymalloc`` + debug    ``pymalloc`` + debug
 Release build, without pymalloc      ``"malloc"``             ``malloc``       
     ``malloc``              ``malloc``
 Debug build, without pymalloc        ``"malloc_debug"``       ``malloc`` + 
debug    ``malloc`` + debug      ``malloc`` + debug
+Release build, with ASan or MSan     ``"malloc"``             ``malloc``       
     ``malloc``              ``malloc``
+Debug build, with ASan or MSan       ``"malloc_debug"``       ``malloc`` + 
debug    ``malloc`` + debug      ``malloc`` + debug
 Free-threaded build                  ``"mimalloc"``           ``mimalloc``     
     ``mimalloc``            ``mimalloc``
 Free-threaded debug build            ``"mimalloc_debug"``     ``mimalloc`` + 
debug  ``mimalloc`` + debug    ``mimalloc`` + debug
 ===================================  =======================  
====================  ======================  ======================
@@ -451,6 +453,10 @@ Legend:
 * "+ debug": with :ref:`debug hooks on the Python memory allocators
   <pymem-debug-hooks>`.
 * "Debug build": :ref:`Python build in debug mode <debug-build>`.
+* "with ASan or MSan": sanitizer build as configured using the
+  :option:`--with-address-sanitizer`,
+  :option:`--with-hwaddress-sanitizer`, and/or
+  :option:`--with-memory-sanitizer` option.
 
 .. _customize-memory-allocators:
 
@@ -705,9 +711,11 @@ This allocator is disabled if Python is configured with the
 :option:`--without-pymalloc` option. It can also be disabled at runtime using
 the :envvar:`PYTHONMALLOC` environment variable (ex: ``PYTHONMALLOC=malloc``).
 
-Typically, it makes sense to disable the pymalloc allocator when building
-Python with AddressSanitizer (:option:`--with-address-sanitizer`) which helps
-uncover low level bugs within the C code.
+The pymalloc allocator is disabled by default when Python is built with
+a sanitizer which does not track pymalloc allocations
+(:option:`--with-address-sanitizer`, :option:`--with-hwaddress-sanitizer`,
+:option:`--with-memory-sanitizer`).
+Use :envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to enable pymalloc.
 
 Customize pymalloc Arena Allocator
 ----------------------------------
diff --git a/Doc/using/configure.rst b/Doc/using/configure.rst
index ab4ec42a575369..8a510a2dc0ba61 100644
--- a/Doc/using/configure.rst
+++ b/Doc/using/configure.rst
@@ -1016,9 +1016,9 @@ Debug options
 .. option:: --with-address-sanitizer
 
    Enable AddressSanitizer memory error detector, ``asan`` (default is no).
-   To improve ASan detection capabilities you may also want to combine this
-   with :option:`--without-pymalloc` to disable the specialized small-object
-   allocator whose allocations are not tracked by ASan.
+
+   When built with ``asan``, Python uses ``malloc`` instead of :ref:`pymalloc 
<pymalloc>` by default.
+   Set :envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to use pymalloc.
 
    .. versionadded:: 3.6
 
@@ -1033,6 +1033,9 @@ Debug options
    <https://clang.llvm.org/docs/HardwareAssistedAddressSanitizerDesign.html>`_
    for more information.
 
+   When built with ``hwasan``, Python uses ``malloc`` instead of 
:ref:`pymalloc <pymalloc>` by default.
+   Set :envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to use pymalloc.
+
    .. versionadded:: next
 
 .. option:: --with-memory-sanitizer
@@ -1043,6 +1046,9 @@ Debug options
    not built with MSan, so either build all dependencies with MSan or disable
    the extension modules that use them in :file:`Modules/Setup.local`.
 
+   When built with ``msan``, Python uses ``malloc`` instead of :ref:`pymalloc 
<pymalloc>` by default.
+   Set :envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to use pymalloc.
+
    .. versionadded:: 3.6
 
 .. option:: --with-undefined-behavior-sanitizer
diff --git a/Include/internal/pycore_pymem_init.h 
b/Include/internal/pycore_pymem_init.h
index 2a0e0817dcc7f8..f89eb8bfc4964c 100644
--- a/Include/internal/pycore_pymem_init.h
+++ b/Include/internal/pycore_pymem_init.h
@@ -36,6 +36,10 @@ extern void _PyMem_MiRawFree(void *, void *);
 extern void* _PyMem_MiRawRealloc(void *, void *, size_t);
 #  undef PYRAW_ALLOC
 #  define PYRAW_ALLOC {NULL, _PyMem_MiRawMalloc, _PyMem_MiRawCalloc, 
_PyMem_MiRawRealloc, _PyMem_MiRawFree}
+#elif defined(_Py_ADDRESS_SANITIZER) || defined(_Py_MEMORY_SANITIZER)
+// Keep in sync with the default allocators in Objects/obmalloc.c.
+#  define PYOBJ_ALLOC PYRAW_ALLOC
+#  define PYMEM_ALLOC PYOBJ_ALLOC
 #elif defined(WITH_PYMALLOC)
 extern void* _PyObject_Malloc(void *, size_t);
 extern void* _PyObject_Calloc(void *, size_t, size_t);
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index 1e303dc3bf6770..0af0debd8150e3 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -845,10 +845,13 @@ def test_xdev(self):
             code = "import _testinternalcapi; 
print(_testinternalcapi.pymem_getallocatorsname())"
             with support.SuppressCrashReport():
                 out = self.run_xdev("-c", code, check_exitcode=False)
-            if support.with_pymalloc():
-                alloc_name = "pymalloc_debug"
-            elif support.Py_GIL_DISABLED:
+            if support.Py_GIL_DISABLED:
                 alloc_name = "mimalloc_debug"
+            elif support.check_sanitizer(address=True, memory=True):
+                # ASan and MSan builds default to malloc, even with pymalloc.
+                alloc_name = "malloc_debug"
+            elif support.with_pymalloc():
+                alloc_name = "pymalloc_debug"
             else:
                 alloc_name = "malloc_debug"
             self.assertEqual(out, alloc_name)
@@ -927,10 +930,15 @@ def test_pythonmalloc(self):
         # Test the PYTHONMALLOC environment variable
         malloc = not support.Py_GIL_DISABLED
         pymalloc = support.with_pymalloc()
+        sanitizer = support.check_sanitizer(address=True, memory=True)
         mimalloc = support.with_mimalloc()
         if support.Py_GIL_DISABLED:
             default_name = 'mimalloc_debug' if support.Py_DEBUG else 'mimalloc'
             default_name_debug = 'mimalloc_debug'
+        elif sanitizer:
+            # ASan and MSan builds default to malloc, even with pymalloc.
+            default_name = 'malloc_debug' if support.Py_DEBUG else 'malloc'
+            default_name_debug = 'malloc_debug'
         elif pymalloc:
             default_name = 'pymalloc_debug' if support.Py_DEBUG else 'pymalloc'
             default_name_debug = 'pymalloc_debug'
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 1ccdcf5a187bbb..c74ca05ca66c9c 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1194,8 +1194,10 @@ def test_debugmallocstats(self):
         # The sysconfig vars are not available on Windows.
         if sys.platform != "win32":
             with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC")
+            with_sanitizer = support.check_sanitizer(address=True, memory=True)
             self.assertIn(b"free PyDictObjects", err)
-            if with_pymalloc:
+            # ASan and MSan builds default to malloc, even with pymalloc.
+            if with_pymalloc and not with_sanitizer:
                 self.assertIn(b'Small block threshold', err)
 
         # The function has no parameter
diff --git 
a/Misc/NEWS.d/next/Build/2026-09-21-19-20-42.gh-issue-136872.a2tSc_.rst 
b/Misc/NEWS.d/next/Build/2026-09-21-19-20-42.gh-issue-136872.a2tSc_.rst
new file mode 100644
index 00000000000000..7e51844695d0e4
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2026-09-21-19-20-42.gh-issue-136872.a2tSc_.rst
@@ -0,0 +1,4 @@
+Python built with :option:`--with-address-sanitizer` or
+:option:`--with-memory-sanitizer` now includes pymalloc, but uses ``malloc``
+as the default memory allocator. Set
+:envvar:`PYTHONMALLOC=pymalloc <PYTHONMALLOC>` to use pymalloc.
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
index 9a41a422467223..841fb796e1ced1 100644
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -433,6 +433,12 @@ void* _PyObject_Realloc(void *ctx, void *ptr, size_t size);
 #  define PYRAW_ALLOC MIMALLOC_RAWALLOC
 #  define PYMEM_ALLOC MIMALLOC_ALLOC
 #  define PYOBJ_ALLOC MIMALLOC_OBJALLOC
+#elif defined(_Py_ADDRESS_SANITIZER) || defined(_Py_MEMORY_SANITIZER)
+// ASan and MSan do not track pymalloc blocks, so use malloc by default.
+// pymalloc can still be selected at runtime.
+#  define PYRAW_ALLOC MALLOC_ALLOC
+#  define PYMEM_ALLOC MALLOC_ALLOC
+#  define PYOBJ_ALLOC MALLOC_ALLOC
 #elif defined(WITH_PYMALLOC)
 #  define PYRAW_ALLOC MALLOC_ALLOC
 #  define PYMEM_ALLOC PYMALLOC_ALLOC
diff --git a/configure b/configure
index c0c0e5b193c206..d6f5319beb7037 100755
--- a/configure
+++ b/configure
@@ -14151,8 +14151,6 @@ then :
 printf "%s\n" "$withval" >&6; }
 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
@@ -14227,11 +14225,6 @@ else case e in #(
 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
@@ -14293,8 +14286,6 @@ else case e in #(
 esac
 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
diff --git a/configure.ac b/configure.ac
index 9e83af6c2d95ca..9ed32b686f033a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3526,8 +3526,6 @@ AC_ARG_WITH([address_sanitizer],
 AC_MSG_RESULT([$withval])
 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])])
 
@@ -3551,11 +3549,6 @@ 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])])
 
@@ -3572,8 +3565,6 @@ 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])])
 

_______________________________________________
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