From: Victor Stinner Date: Wed, 10 Jun 2026 19:39:03 +0000 (+0200) Subject: gh-151278: Fix test_faulthandler on UBSan (#151279) X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e60c42dc3f5a8dd9b10bc9a8a028ef2765469650;p=thirdparty%2FPython%2Fcpython.git gh-151278: Fix test_faulthandler on UBSan (#151279) * Py_FatalError() no longer calls _PyFaulthandler_Fini() if it doesn't hold the GIL. * Skip test_faulthandler tests raising signals if run with UBSan. * Enable test_faulthandler in GitHub Action "Reusable Sanitizer". --- diff --git a/.github/workflows/reusable-san.yml b/.github/workflows/reusable-san.yml index d1e9cb963669..6127c7e1c053 100644 --- a/.github/workflows/reusable-san.yml +++ b/.github/workflows/reusable-san.yml @@ -82,13 +82,13 @@ jobs: run: make -j4 - name: Display build info run: make pythoninfo - # test_{capi,faulthandler} are skipped under UBSan because + # test_capi is skipped under UBSan because # they raise signals that UBSan with halt_on_error=1 intercepts. - name: Tests run: >- ./python -m test ${{ inputs.sanitizer == 'TSan' && '--tsan' || '' }} - ${{ inputs.sanitizer == 'UBSan' && '-x test_capi -x test_faulthandler' || '' }} + ${{ inputs.sanitizer == 'UBSan' && '-x test_capi' || '' }} -j4 -W - name: Parallel tests if: >- diff --git a/Lib/test/test_faulthandler.py b/Lib/test/test_faulthandler.py index 11df59f2346f..5a493a4fd956 100644 --- a/Lib/test/test_faulthandler.py +++ b/Lib/test/test_faulthandler.py @@ -33,6 +33,11 @@ CURRENT_THREAD_ID = fr'Current thread 0x[0-9a-f]+{THREAD_NAME}' CURRENT_THREAD_HEADER = fr'{CURRENT_THREAD_ID} \(most recent call first\):' +def skip_if_sanitizer_signal(signame): + return support.skip_if_sanitizer(f"TSAN/UBSan itercepts {signame}", + thread=True, ub=True) + + def expected_traceback(lineno1, lineno2, header, min_count=1): regex = header regex += ' File "", line %s in func\n' % lineno1 @@ -224,7 +229,7 @@ class FaultHandlerTests(unittest.TestCase): func='faulthandler_fatal_error_thread', py_fatal_error=True) - @support.skip_if_sanitizer("TSAN itercepts SIGABRT", thread=True) + @skip_if_sanitizer_signal("SIGABRT") def test_sigabrt(self): self.check_fatal_error(""" import faulthandler @@ -236,7 +241,7 @@ class FaultHandlerTests(unittest.TestCase): @unittest.skipIf(sys.platform == 'win32', "SIGFPE cannot be caught on Windows") - @support.skip_if_sanitizer("TSAN itercepts SIGFPE", thread=True) + @skip_if_sanitizer_signal("SIGFPE") def test_sigfpe(self): self.check_fatal_error(""" import faulthandler @@ -248,7 +253,7 @@ class FaultHandlerTests(unittest.TestCase): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(signal, 'SIGBUS'), 'need signal.SIGBUS') - @support.skip_if_sanitizer("TSAN itercepts SIGBUS", thread=True) + @skip_if_sanitizer_signal("SIGBUS") @skip_segfault_on_android def test_sigbus(self): self.check_fatal_error(""" @@ -263,7 +268,7 @@ class FaultHandlerTests(unittest.TestCase): @unittest.skipIf(_testcapi is None, 'need _testcapi') @unittest.skipUnless(hasattr(signal, 'SIGILL'), 'need signal.SIGILL') - @support.skip_if_sanitizer("TSAN itercepts SIGILL", thread=True) + @skip_if_sanitizer_signal("SIGILL") @skip_segfault_on_android def test_sigill(self): self.check_fatal_error(""" diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 97511d2daf2c..2f7f10f523c2 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -3731,7 +3731,9 @@ fatal_error(int fd, int header, const char *prefix, const char *msg, This function already did its best to display a traceback. Disable faulthandler to prevent writing a second traceback on abort(). */ - _PyFaulthandler_Fini(); + if (has_tstate_and_gil) { + _PyFaulthandler_Fini(); + } /* Check if the current Python thread hold the GIL */ if (has_tstate_and_gil) {