diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py b/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py index c412c3e780..84616e58a9 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_traceback.py @@ -37,6 +37,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import ast +import subprocess import sys @@ -659,6 +660,34 @@ def test_faulthandler_many_threads(): assert len(ids) == 1, f"Interleaved output detected in block {header!r} with multiple thread func ids: {ids}" +def test_faulthandler_sigsegv_builtin(): + import faulthandler + + try: + if not __graalpython__.is_native or __graalpython__.posix_module_backend() == "java": + return + except NameError: + pass # CPython + + def assert_fatal_faulthandler_call(name, *args): + assert hasattr(faulthandler, name) + code = f"import faulthandler; faulthandler.{name}({', '.join(map(repr, args))})" + proc = subprocess.run( + [sys.executable, "-c", code], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + assert proc.returncode != 0 + assert proc.returncode != 1, ( + f"expected fatal signal path for faulthandler.{name}{args}, " + f"got regular Python error exit {proc.returncode}" + ) + + for release_gil in (False, True): + assert_fatal_faulthandler_call("_sigsegv", release_gil) + assert_fatal_faulthandler_call("_sigabrt") + + def test_location_from_ast(): m = compile("a = 1\nx", "", "exec", flags=ast.PyCF_ONLY_AST) diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_concurrent_futures.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_concurrent_futures.txt index 41a67a60a2..54e7c8fba4 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_concurrent_futures.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_concurrent_futures.txt @@ -7,7 +7,6 @@ test.test_concurrent_futures.test_as_completed.ThreadPoolAsCompletedTest.test_co test.test_concurrent_futures.test_as_completed.ThreadPoolAsCompletedTest.test_duplicate_futures @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_as_completed.ThreadPoolAsCompletedTest.test_future_times_out @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_as_completed.ThreadPoolAsCompletedTest.test_no_timeout @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_crash_at_task_unpickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_error_at_task_pickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_error_at_task_unpickle @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_error_during_func_exec_on_worker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github @@ -18,6 +17,7 @@ test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest. test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_exit_during_result_pickle_on_worker @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_exit_during_result_unpickle_in_result_handler @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github # Transiently times out GR-65714 +!test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_crash_at_task_unpickle !test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_gh105829_should_not_deadlock_if_wakeup_pipe_full !test.test_concurrent_futures.test_deadlock.ProcessPoolSpawnExecutorDeadlockTest.test_shutdown_deadlock_pickle !test.test_concurrent_futures.test_future.FutureTests.test_cancel diff --git a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_faulthandler.txt b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_faulthandler.txt index b975281c44..c5a039a156 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_faulthandler.txt +++ b/graalpython/com.oracle.graal.python.test/src/tests/unittest_tags/test_faulthandler.txt @@ -1,7 +1,7 @@ test.test_faulthandler.FaultHandlerTests.test_cancel_later_without_dump_traceback_later @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github -test.test_faulthandler.FaultHandlerTests.test_disable @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github test.test_faulthandler.FaultHandlerTests.test_disabled_by_default @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github test.test_faulthandler.FaultHandlerTests.test_is_enabled @ darwin-arm64,linux-aarch64,linux-aarch64-github,linux-x86_64,linux-x86_64-github,win32-AMD64,win32-AMD64-github # Disabled since signaling isn't stable during parallel tests +!test.test_faulthandler.FaultHandlerTests.test_disable !test.test_faulthandler.FaultHandlerTests.test_sigbus !test.test_faulthandler.FaultHandlerTests.test_sigill diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java index 40d1d821e7..4dd8e5a978 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java @@ -69,16 +69,21 @@ import com.oracle.graal.python.lib.PyTimeFromObjectNode; import com.oracle.graal.python.lib.PyTimeFromObjectNode.RoundType; import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode; +import com.oracle.graal.python.nodes.function.builtins.PythonUnaryClinicBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; +import com.oracle.graal.python.runtime.GilNode; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PosixSupportLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.ExceptionUtils; @@ -95,6 +100,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; @@ -235,6 +241,62 @@ static int fileno(VirtualFrame frame, Node inliningTarget, Object file, } } + @Builtin(name = "_sigsegv", minNumOfPositionalArgs = 0, parameterNames = {"release_gil"}) + @ArgumentClinic(name = "release_gil", conversion = ArgumentClinic.ClinicConversion.Boolean, defaultValue = "false") + @GenerateNodeFactory + abstract static class SigSegvNode extends PythonUnaryClinicBuiltinNode { + @Specialization + static PNone doIt(VirtualFrame frame, boolean releaseGil, + @Bind PythonContext context, + @Bind Node inliningTarget, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached GilNode gil, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + return raiseFatalSignal(frame, context, inliningTarget, posixLib, gil, constructAndRaiseNode, "SEGV", releaseGil); + } + + @Override + protected ArgumentClinicProvider getArgumentClinic() { + return FaulthandlerModuleBuiltinsClinicProviders.SigSegvNodeClinicProviderGen.INSTANCE; + } + } + + @Builtin(name = "_sigabrt", minNumOfPositionalArgs = 0) + @GenerateNodeFactory + abstract static class SigAbrtNode extends PythonBuiltinNode { + @Specialization + static PNone doIt(VirtualFrame frame, + @Bind PythonContext context, + @Bind Node inliningTarget, + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib, + @Cached GilNode gil, + @Cached PConstructAndRaiseNode.Lazy constructAndRaiseNode) { + return raiseFatalSignal(frame, context, inliningTarget, posixLib, gil, constructAndRaiseNode, "ABRT", false); + } + } + + private static PNone raiseFatalSignal(VirtualFrame frame, PythonContext context, Node inliningTarget, PosixSupportLibrary posixLib, GilNode gil, + PConstructAndRaiseNode.Lazy constructAndRaiseNode, String signalName, boolean releaseGil) { + try { + int signum = SignalModuleBuiltins.signalFromName(context, signalName); + if (releaseGil) { + gil.release(true); + } + try { + posixLib.signalSelf(context.getPosixSupport(), signum); + } finally { + if (releaseGil) { + gil.acquire(); + } + } + } catch (PosixException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e); + } catch (UnsupportedPosixFeatureException e) { + throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorUnsupported(frame, e); + } + return PNone.NONE; + } + @Builtin(name = "dump_traceback_later", minNumOfPositionalArgs = 2, declaresExplicitSelf = true, parameterNames = {"$mod", "timeout", "repeat", "file", "exit"}) @ArgumentClinic(name = "repeat", conversion = ArgumentClinic.ClinicConversion.IntToBoolean, defaultValue = "false") @ArgumentClinic(name = "exit", conversion = ArgumentClinic.ClinicConversion.IntToBoolean, defaultValue = "false") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java index cd9bbdd9ac..88ec3a30f9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -2014,6 +2014,12 @@ public void kill(long pid, int signal, } } + @ExportMessage + @SuppressWarnings("static-method") + public void signalSelf(int signal) { + throw createUnsupportedFeature("signalSelf"); + } + @ExportMessage @SuppressWarnings("static-method") public long killpg(long pgid, int signal) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java index 3ee34664e8..e7371fadb9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -752,6 +752,17 @@ final void kill(long pid, int signal, } } + @ExportMessage + final void signalSelf(int signal, + @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { + logEnter("signalSelf", "%d", signal); + try { + lib.signalSelf(delegate, signal); + } catch (PosixException e) { + throw logException("signalSelf", e); + } + } + @ExportMessage final void killpg(long pgid, int signal, @CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java index 536aa98596..1bf99341f3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java @@ -89,6 +89,7 @@ import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.logging.Level; +import org.graalvm.nativeimage.ImageInfo; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.PythonOS; import com.oracle.graal.python.builtins.PythonBuiltinClassType; @@ -229,6 +230,7 @@ private enum PosixNativeFunction { get_blocking("(sint32):sint32"), set_blocking("(sint32, sint32):sint32"), get_terminal_size("(sint32, [sint32]):sint32"), + signal_self("(sint32):sint32"), call_kill("(sint64, sint32):sint32"), call_killpg("(sint64, sint32):sint32"), call_waitpid("(sint64, [sint32], sint32):sint64"), @@ -1164,6 +1166,18 @@ public void kill(long pid, int signal, } } + @ExportMessage + public void signalSelf(int signal, + @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { + if (!ImageInfo.inImageRuntimeCode()) { + throw new UnsupportedPosixFeatureException("self-signals are only supported in native standalone"); + } + int res = invokeNode.callInt(this, PosixNativeFunction.signal_self, signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(invokeNode); + } + } + @ExportMessage public void killpg(long pgid, int signal, @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java index 54e6e80290..14033ed690 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -258,6 +258,8 @@ public abstract class PosixSupportLibrary extends Library { public abstract Object readlinkat(Object receiver, int dirFd, Object path) throws PosixException; + public abstract void signalSelf(Object receiver, int signal) throws PosixException; + public abstract void kill(Object receiver, long pid, int signal) throws PosixException; public abstract void killpg(Object receiver, long pid, int signal) throws PosixException; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java index 518aefd17e..9b36dc9fd0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -588,6 +588,13 @@ final Object readlinkat(int dirFd, Object path, return nativeLib.readlinkat(nativePosixSupport, dirFd, path); } + @ExportMessage + final void signalSelf(int signal, + @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { + checkNotInPreInitialization(); + nativeLib.signalSelf(nativePosixSupport, signal); + } + @ExportMessage final void kill(long pid, int signal, @CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException { diff --git a/graalpython/python-libposix/src/posix.c b/graalpython/python-libposix/src/posix.c index 1d34e97482..5d6d863e95 100644 --- a/graalpython/python-libposix/src/posix.c +++ b/graalpython/python-libposix/src/posix.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -77,6 +77,8 @@ #include #include +int32_t signal_self_segv(void); + #ifdef __APPLE__ #include #else @@ -602,6 +604,20 @@ int32_t call_kill(int64_t pid, int32_t signal) { return kill(pid, signal); } +int32_t signal_self(int32_t signal) { + switch (signal) { + case SIGABRT: + abort(); + break; + case SIGSEGV: + return signal_self_segv(); + default: + errno = EINVAL; + return -1; + } + _exit(128 + signal); +} + int32_t call_killpg(int64_t pgid, int32_t signal) { return killpg(pgid, signal); } diff --git a/graalpython/python-libposix/src/posix_signal_self_segv.c b/graalpython/python-libposix/src/posix_signal_self_segv.c new file mode 100644 index 0000000000..ef1d6f2aab --- /dev/null +++ b/graalpython/python-libposix/src/posix_signal_self_segv.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#if defined(_MSC_VER) +#pragma optimize("", off) +#define SIGNAL_SELF_SEGV_ATTR __declspec(noinline) +#elif defined(__clang__) +#pragma clang optimize off +#define SIGNAL_SELF_SEGV_ATTR __attribute__((optnone, noinline)) +#elif defined(__GNUC__) +#define SIGNAL_SELF_SEGV_ATTR __attribute__((optimize("O0"), noinline, noclone)) +#else +#define SIGNAL_SELF_SEGV_ATTR +#endif + +/* + * Keep the deliberate SIGSEGV in its own translation unit and discourage + * inlining/optimization to minimize the optimizer's freedom around the + * undefined null-dereference below. + */ +SIGNAL_SELF_SEGV_ATTR int32_t signal_self_segv(void) { + volatile int *p = (volatile int *)(uintptr_t)0; + *p = 1; + return 0; +} + +#if defined(_MSC_VER) +#pragma optimize("", on) +#elif defined(__clang__) +#pragma clang optimize on +#endif