]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-64783: Fix signal.NSIG value on FreeBSD (#91929)
authorVictor Stinner <vstinner@python.org>
Mon, 25 Apr 2022 22:13:31 +0000 (00:13 +0200)
committerGitHub <noreply@github.com>
Mon, 25 Apr 2022 22:13:31 +0000 (00:13 +0200)
Fix signal.NSIG value on FreeBSD to accept signal numbers greater
than 32, like signal.SIGRTMIN and signal.SIGRTMAX.

* Add Py_NSIG constant.
* Add pycore_signal.h internal header file.
* _Py_Sigset_Converter() now includes the range of valid signals in
  the error message.

Doc/library/signal.rst
Include/internal/pycore_pylifecycle.h
Include/internal/pycore_signal.h [new file with mode: 0644]
Lib/test/test_signal.py
Makefile.pre.in
Misc/NEWS.d/next/Library/2022-04-25-18-30-20.gh-issue-64783.HFtERN.rst [new file with mode: 0644]
Modules/faulthandler.c
Modules/posixmodule.c
Modules/signalmodule.c
PCbuild/pythoncore.vcxproj
PCbuild/pythoncore.vcxproj.filters

index fdc9846f6642a70262f15bf0ce719a3460417a51..678411d4f17056c631d88c752dcc3f1cf29a2ae9 100644 (file)
@@ -266,6 +266,7 @@ The variables defined in the :mod:`signal` module are:
 .. data:: NSIG
 
    One more than the number of the highest signal number.
+   Use :func:`valid_signals` to get valid signal numbers.
 
 
 .. data:: ITIMER_REAL
index 295505f1f37359d0c50f669fb0349bc4c5e12aad..b4718b8ade2354aabad2ee075a4ccb9fe4cb47b2 100644 (file)
@@ -8,24 +8,8 @@ extern "C" {
 #  error "this header requires Py_BUILD_CORE define"
 #endif
 
-#ifdef HAVE_SIGNAL_H
-#include <signal.h>
-#endif
-
 #include "pycore_runtime.h"       // _PyRuntimeState
 
-#ifndef NSIG
-# if defined(_NSIG)
-#  define NSIG _NSIG            /* For BSD/SysV */
-# elif defined(_SIGMAX)
-#  define NSIG (_SIGMAX + 1)    /* For QNX */
-# elif defined(SIGMAX)
-#  define NSIG (SIGMAX + 1)     /* For djgpp */
-# else
-#  define NSIG 64               /* Use a reasonable default value */
-# endif
-#endif
-
 /* Forward declarations */
 struct _PyArgv;
 struct pyruntimestate;
diff --git a/Include/internal/pycore_signal.h b/Include/internal/pycore_signal.h
new file mode 100644 (file)
index 0000000..b921dd1
--- /dev/null
@@ -0,0 +1,35 @@
+// Define Py_NSIG constant for signal handling.
+
+#ifndef Py_INTERNAL_SIGNAL_H
+#define Py_INTERNAL_SIGNAL_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef Py_BUILD_CORE
+#  error "this header requires Py_BUILD_CORE define"
+#endif
+
+#include <signal.h>                // NSIG
+
+#ifdef _SIG_MAXSIG
+   // gh-91145: On FreeBSD, <signal.h> defines NSIG as 32: it doesn't include
+   // realtime signals: [SIGRTMIN,SIGRTMAX]. Use _SIG_MAXSIG instead. For
+   // example on x86-64 FreeBSD 13, SIGRTMAX is 126 and _SIG_MAXSIG is 128.
+#  define Py_NSIG _SIG_MAXSIG
+#elif defined(NSIG)
+#  define Py_NSIG NSIG
+#elif defined(_NSIG)
+#  define Py_NSIG _NSIG            // BSD/SysV
+#elif defined(_SIGMAX)
+#  define Py_NSIG (_SIGMAX + 1)    // QNX
+#elif defined(SIGMAX)
+#  define Py_NSIG (SIGMAX + 1)     // djgpp
+#else
+#  define Py_NSIG 64               // Use a reasonable default value
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif  // !Py_INTERNAL_SIGNAL_H
index ea13c59ec711e607f69bcb7982e71f4f457efdf5..d38992db7b84d22503a88dea92dc7775cb1f0ae0 100644 (file)
@@ -116,6 +116,16 @@ class PosixTests(unittest.TestCase):
         self.assertNotIn(signal.NSIG, s)
         self.assertLess(len(s), signal.NSIG)
 
+        # gh-91145: Make sure that all SIGxxx constants exposed by the Python
+        # signal module have a number in the [0; signal.NSIG-1] range.
+        for name in dir(signal):
+            if not name.startswith("SIG"):
+                continue
+            with self.subTest(name=name):
+                signum = getattr(signal, name)
+                self.assertGreaterEqual(signum, 0)
+                self.assertLess(signum, signal.NSIG)
+
     @unittest.skipUnless(sys.executable, "sys.executable required.")
     @support.requires_subprocess()
     def test_keyboard_interrupt_exit_code(self):
index d9f821dd14e172d61b258006382b81096436e360..3952f5b542b430c174a607cafd22165be5ea2530 100644 (file)
@@ -1622,6 +1622,7 @@ PYTHON_HEADERS= \
                $(srcdir)/Include/internal/pycore_pystate.h \
                $(srcdir)/Include/internal/pycore_runtime.h \
                $(srcdir)/Include/internal/pycore_runtime_init.h \
+               $(srcdir)/Include/internal/pycore_signal.h \
                $(srcdir)/Include/internal/pycore_sliceobject.h \
                $(srcdir)/Include/internal/pycore_strhex.h \
                $(srcdir)/Include/internal/pycore_structseq.h \
diff --git a/Misc/NEWS.d/next/Library/2022-04-25-18-30-20.gh-issue-64783.HFtERN.rst b/Misc/NEWS.d/next/Library/2022-04-25-18-30-20.gh-issue-64783.HFtERN.rst
new file mode 100644 (file)
index 0000000..41814a6
--- /dev/null
@@ -0,0 +1,3 @@
+Fix :data:`signal.NSIG` value on FreeBSD to accept signal numbers greater than
+32, like :data:`signal.SIGRTMIN` and :data:`signal.SIGRTMAX`. Patch by Victor
+Stinner.
index 91f96665a4a3082a5491db7f39d6bea184ca3f7c..4f709edb479a2b4650dd7c754cd10d077e4dc0cb 100644 (file)
@@ -2,6 +2,7 @@
 #include "pycore_initconfig.h"    // _PyStatus_ERR
 #include "pycore_pyerrors.h"      // _Py_DumpExtensionModules
 #include "pycore_pystate.h"       // _PyThreadState_GET()
+#include "pycore_signal.h"        // Py_NSIG
 #include "pycore_traceback.h"     // _Py_DumpTracebackThreads
 
 #include "frameobject.h"
@@ -115,19 +116,6 @@ typedef struct {
 
 static user_signal_t *user_signals;
 
-/* the following macros come from Python: Modules/signalmodule.c */
-#ifndef NSIG
-# if defined(_NSIG)
-#  define NSIG _NSIG            /* For BSD/SysV */
-# elif defined(_SIGMAX)
-#  define NSIG (_SIGMAX + 1)    /* For QNX */
-# elif defined(SIGMAX)
-#  define NSIG (SIGMAX + 1)     /* For djgpp */
-# else
-#  define NSIG 64               /* Use a reasonable default value */
-# endif
-#endif
-
 static void faulthandler_user(int signum);
 #endif /* FAULTHANDLER_USER */
 
@@ -896,7 +884,7 @@ check_signum(int signum)
             return 0;
         }
     }
-    if (signum < 1 || NSIG <= signum) {
+    if (signum < 1 || Py_NSIG <= signum) {
         PyErr_SetString(PyExc_ValueError, "signal number out of range");
         return 0;
     }
@@ -935,7 +923,7 @@ faulthandler_register_py(PyObject *self,
         return NULL;
 
     if (user_signals == NULL) {
-        user_signals = PyMem_Calloc(NSIG, sizeof(user_signal_t));
+        user_signals = PyMem_Calloc(Py_NSIG, sizeof(user_signal_t));
         if (user_signals == NULL)
             return PyErr_NoMemory();
     }
@@ -1215,7 +1203,7 @@ faulthandler_traverse(PyObject *module, visitproc visit, void *arg)
     Py_VISIT(thread.file);
 #ifdef FAULTHANDLER_USER
     if (user_signals != NULL) {
-        for (size_t signum=0; signum < NSIG; signum++)
+        for (size_t signum=0; signum < Py_NSIG; signum++)
             Py_VISIT(user_signals[signum].file);
     }
 #endif
@@ -1416,7 +1404,7 @@ void _PyFaulthandler_Fini(void)
 #ifdef FAULTHANDLER_USER
     /* user */
     if (user_signals != NULL) {
-        for (size_t signum=0; signum < NSIG; signum++) {
+        for (size_t signum=0; signum < Py_NSIG; signum++) {
             faulthandler_unregister(&user_signals[signum], signum);
         }
         PyMem_Free(user_signals);
index a9132a78994ce44fcd0e5573e979c083bfc9f342..a2ea5079969e9f2efd9508f6b8e30d1f9e20d82c 100644 (file)
@@ -29,6 +29,7 @@
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
 #include "pycore_object.h"        // _PyObject_LookupSpecial()
 #include "pycore_pystate.h"       // _PyInterpreterState_GET()
+#include "pycore_signal.h"        // Py_NSIG
 
 #include "structmember.h"         // PyMemberDef
 #ifndef MS_WINDOWS
@@ -1503,10 +1504,11 @@ _Py_Sigset_Converter(PyObject *obj, void *addr)
     while ((item = PyIter_Next(iterator)) != NULL) {
         signum = PyLong_AsLongAndOverflow(item, &overflow);
         Py_DECREF(item);
-        if (signum <= 0 || signum >= NSIG) {
+        if (signum <= 0 || signum >= Py_NSIG) {
             if (overflow || signum != -1 || !PyErr_Occurred()) {
                 PyErr_Format(PyExc_ValueError,
-                             "signal number %ld out of range", signum);
+                             "signal number %ld out of range [1; %i]",
+                             signum, Py_NSIG - 1);
             }
             goto error;
         }
index 02c58ff538ecdce8f34565d62adb7a0cecb13599..b602338e4f91e6440a31c78647fb11f66ac801e5 100644 (file)
@@ -7,13 +7,13 @@
 #include "pycore_atomic.h"        // _Py_atomic_int
 #include "pycore_call.h"          // _PyObject_Call()
 #include "pycore_ceval.h"         // _PyEval_SignalReceived()
+#include "pycore_emscripten_signal.h"  // _Py_CHECK_EMSCRIPTEN_SIGNALS
 #include "pycore_fileutils.h"     // _Py_BEGIN_SUPPRESS_IPH
 #include "pycore_frame.h"         // _PyInterpreterFrame
 #include "pycore_moduleobject.h"  // _PyModule_GetState()
 #include "pycore_pyerrors.h"      // _PyErr_SetString()
-#include "pycore_pylifecycle.h"   // NSIG
 #include "pycore_pystate.h"       // _PyThreadState_GET()
-#include "pycore_emscripten_signal.h"  // _Py_CHECK_EMSCRIPTEN_SIGNALS
+#include "pycore_signal.h"        // Py_NSIG
 
 #ifndef MS_WINDOWS
 #  include "posixmodule.h"
@@ -106,7 +106,7 @@ static volatile struct {
      * (even though it would probably be otherwise, anyway).
      */
     _Py_atomic_address func;
-} Handlers[NSIG];
+} Handlers[Py_NSIG];
 
 #ifdef MS_WINDOWS
 #define INVALID_FD ((SOCKET_T)-1)
@@ -542,7 +542,7 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
                          "of the main interpreter");
         return NULL;
     }
-    if (signalnum < 1 || signalnum >= NSIG) {
+    if (signalnum < 1 || signalnum >= Py_NSIG) {
         _PyErr_SetString(tstate, PyExc_ValueError,
                          "signal number out of range");
         return NULL;
@@ -601,7 +601,7 @@ signal_getsignal_impl(PyObject *module, int signalnum)
 /*[clinic end generated code: output=35b3e0e796fd555e input=ac23a00f19dfa509]*/
 {
     PyObject *old_handler;
-    if (signalnum < 1 || signalnum >= NSIG) {
+    if (signalnum < 1 || signalnum >= Py_NSIG) {
         PyErr_SetString(PyExc_ValueError,
                         "signal number out of range");
         return NULL;
@@ -634,7 +634,7 @@ signal_strsignal_impl(PyObject *module, int signalnum)
 {
     const char *res;
 
-    if (signalnum < 1 || signalnum >= NSIG) {
+    if (signalnum < 1 || signalnum >= Py_NSIG) {
         PyErr_SetString(PyExc_ValueError,
                 "signal number out of range");
         return NULL;
@@ -712,7 +712,7 @@ static PyObject *
 signal_siginterrupt_impl(PyObject *module, int signalnum, int flag)
 /*[clinic end generated code: output=063816243d85dd19 input=4160acacca3e2099]*/
 {
-    if (signalnum < 1 || signalnum >= NSIG) {
+    if (signalnum < 1 || signalnum >= Py_NSIG) {
         PyErr_SetString(PyExc_ValueError,
                         "signal number out of range");
         return NULL;
@@ -964,7 +964,7 @@ sigset_to_set(sigset_t mask)
     if (result == NULL)
         return NULL;
 
-    for (sig = 1; sig < NSIG; sig++) {
+    for (sig = 1; sig < Py_NSIG; sig++) {
         if (sigismember(&mask, sig) != 1)
             continue;
 
@@ -1439,13 +1439,15 @@ the first is the signal number, the second is the interrupted stack frame.");
 static int
 signal_add_constants(PyObject *module)
 {
+    if (PyModule_AddIntConstant(module, "NSIG", Py_NSIG) < 0) {
+        return -1;
+    }
+
 #define ADD_INT_MACRO(macro) \
     if (PyModule_AddIntConstant(module, #macro, macro) < 0) { \
         return -1; \
     }
 
-    ADD_INT_MACRO(NSIG);
-
     // SIG_xxx pthread_sigmask() constants
 #ifdef SIG_BLOCK
     ADD_INT_MACRO(SIG_BLOCK);
@@ -1605,7 +1607,7 @@ static int
 signal_get_set_handlers(signal_state_t *state, PyObject *mod_dict)
 {
     // Get signal handlers
-    for (int signum = 1; signum < NSIG; signum++) {
+    for (int signum = 1; signum < Py_NSIG; signum++) {
         void (*c_handler)(int) = PyOS_getsig(signum);
         PyObject *func;
         if (c_handler == SIG_DFL) {
@@ -1762,7 +1764,7 @@ _PySignal_Fini(void)
     signal_state_t *state = &signal_global_state;
 
     // Restore default signals and clear handlers
-    for (int signum = 1; signum < NSIG; signum++) {
+    for (int signum = 1; signum < Py_NSIG; signum++) {
         PyObject *func = get_handler(signum);
         _Py_atomic_store_relaxed(&Handlers[signum].tripped, 0);
         set_handler(signum, NULL);
@@ -1828,7 +1830,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate)
 
     _PyInterpreterFrame *frame = tstate->cframe->current_frame;
     signal_state_t *state = &signal_global_state;
-    for (int i = 1; i < NSIG; i++) {
+    for (int i = 1; i < Py_NSIG; i++) {
         if (!_Py_atomic_load_relaxed(&Handlers[i].tripped)) {
             continue;
         }
@@ -1905,7 +1907,7 @@ _PyErr_CheckSignals(void)
 int
 PyErr_SetInterruptEx(int signum)
 {
-    if (signum < 1 || signum >= NSIG) {
+    if (signum < 1 || signum >= Py_NSIG) {
         return -1;
     }
 
@@ -1995,7 +1997,7 @@ _PySignal_Init(int install_signal_handlers)
     }
 #endif
 
-    for (int signum = 1; signum < NSIG; signum++) {
+    for (int signum = 1; signum < Py_NSIG; signum++) {
         _Py_atomic_store_relaxed(&Handlers[signum].tripped, 0);
     }
 
@@ -2045,7 +2047,7 @@ _clear_pending_signals(void)
     }
 
     _Py_atomic_store(&is_tripped, 0);
-    for (int i = 1; i < NSIG; ++i) {
+    for (int i = 1; i < Py_NSIG; ++i) {
         _Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
     }
 }
index 78bbec1e104e486b70e29a7adc27be27ffe7e583..3ce116d2babb08e003304da75e20c33433e7325a 100644 (file)
     <ClInclude Include="..\Include\internal\pycore_pystate.h" />
     <ClInclude Include="..\Include\internal\pycore_runtime.h" />
     <ClInclude Include="..\Include\internal\pycore_runtime_init.h" />
+    <ClInclude Include="..\Include\internal\pycore_signal.h" />
     <ClInclude Include="..\Include\internal\pycore_sliceobject.h" />
     <ClInclude Include="..\Include\internal\pycore_strhex.h" />
     <ClInclude Include="..\Include\internal\pycore_structseq.h" />
index 0a9d5454ba957aab5236c0a4138c8646e81d3c80..542d551045686dbfaeb7081430a12201c0b87035 100644 (file)
     <ClInclude Include="..\Include\internal\pycore_runtime_init.h">
       <Filter>Include\internal</Filter>
     </ClInclude>
+    <ClInclude Include="..\Include\internal\pycore_signal.h">
+      <Filter>Include\internal</Filter>
+    </ClInclude>
     <ClInclude Include="..\Include\internal\pycore_sliceobject.h">
       <Filter>Include\internal</Filter>
     </ClInclude>