bpo-33441: Make the sigset_t converter available in other modules. (GH-6720)

* Expose the sigset_t converter via private API _Py_Sigset_Converter().
* Use Argument Clinic for parsing sigset_t in signalmodule.c.
* Raise ValueError instead OverflowError for integers out of
  the C long range.

Based on patch by Pablo Galindo Salgado.
This commit is contained in:
Serhiy Storchaka 2018-05-08 07:48:50 +03:00 committed by GitHub
parent a3f19c3f52
commit d54cfb160c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 142 additions and 105 deletions

View File

@ -943,6 +943,10 @@ class PendingSignalsTests(unittest.TestCase):
self.assertRaises(OSError, signal.pthread_sigmask, 1700, [])
with self.assertRaises(ValueError):
signal.pthread_sigmask(signal.SIG_BLOCK, [signal.NSIG])
with self.assertRaises(ValueError):
signal.pthread_sigmask(signal.SIG_BLOCK, [0])
with self.assertRaises(ValueError):
signal.pthread_sigmask(signal.SIG_BLOCK, [1<<1000])
@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')

View File

@ -278,17 +278,17 @@ PyDoc_STRVAR(signal_pthread_sigmask__doc__,
{"pthread_sigmask", (PyCFunction)signal_pthread_sigmask, METH_FASTCALL, signal_pthread_sigmask__doc__},
static PyObject *
signal_pthread_sigmask_impl(PyObject *module, int how, PyObject *mask);
signal_pthread_sigmask_impl(PyObject *module, int how, sigset_t mask);
static PyObject *
signal_pthread_sigmask(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
int how;
PyObject *mask;
sigset_t mask;
if (!_PyArg_ParseStack(args, nargs, "iO:pthread_sigmask",
&how, &mask)) {
if (!_PyArg_ParseStack(args, nargs, "iO&:pthread_sigmask",
&how, _Py_Sigset_Converter, &mask)) {
goto exit;
}
return_value = signal_pthread_sigmask_impl(module, how, mask);
@ -339,6 +339,24 @@ PyDoc_STRVAR(signal_sigwait__doc__,
#define SIGNAL_SIGWAIT_METHODDEF \
{"sigwait", (PyCFunction)signal_sigwait, METH_O, signal_sigwait__doc__},
static PyObject *
signal_sigwait_impl(PyObject *module, sigset_t sigset);
static PyObject *
signal_sigwait(PyObject *module, PyObject *arg)
{
PyObject *return_value = NULL;
sigset_t sigset;
if (!PyArg_Parse(arg, "O&:sigwait", _Py_Sigset_Converter, &sigset)) {
goto exit;
}
return_value = signal_sigwait_impl(module, sigset);
exit:
return return_value;
}
#endif /* defined(HAVE_SIGWAIT) */
#if (defined(HAVE_SIGFILLSET) || defined(MS_WINDOWS))
@ -379,6 +397,24 @@ PyDoc_STRVAR(signal_sigwaitinfo__doc__,
#define SIGNAL_SIGWAITINFO_METHODDEF \
{"sigwaitinfo", (PyCFunction)signal_sigwaitinfo, METH_O, signal_sigwaitinfo__doc__},
static PyObject *
signal_sigwaitinfo_impl(PyObject *module, sigset_t sigset);
static PyObject *
signal_sigwaitinfo(PyObject *module, PyObject *arg)
{
PyObject *return_value = NULL;
sigset_t sigset;
if (!PyArg_Parse(arg, "O&:sigwaitinfo", _Py_Sigset_Converter, &sigset)) {
goto exit;
}
return_value = signal_sigwaitinfo_impl(module, sigset);
exit:
return return_value;
}
#endif /* defined(HAVE_SIGWAITINFO) */
#if defined(HAVE_SIGTIMEDWAIT)
@ -395,19 +431,18 @@ PyDoc_STRVAR(signal_sigtimedwait__doc__,
{"sigtimedwait", (PyCFunction)signal_sigtimedwait, METH_FASTCALL, signal_sigtimedwait__doc__},
static PyObject *
signal_sigtimedwait_impl(PyObject *module, PyObject *sigset,
signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
PyObject *timeout_obj);
static PyObject *
signal_sigtimedwait(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
PyObject *sigset;
sigset_t sigset;
PyObject *timeout_obj;
if (!_PyArg_UnpackStack(args, nargs, "sigtimedwait",
2, 2,
&sigset, &timeout_obj)) {
if (!_PyArg_ParseStack(args, nargs, "O&O:sigtimedwait",
_Py_Sigset_Converter, &sigset, &timeout_obj)) {
goto exit;
}
return_value = signal_sigtimedwait_impl(module, sigset, timeout_obj);
@ -499,4 +534,4 @@ exit:
#ifndef SIGNAL_PTHREAD_KILL_METHODDEF
#define SIGNAL_PTHREAD_KILL_METHODDEF
#endif /* !defined(SIGNAL_PTHREAD_KILL_METHODDEF) */
/*[clinic end generated code: output=f35d79e0cfee3f1b input=a9049054013a1b77]*/
/*[clinic end generated code: output=549f0efdc7405834 input=a9049054013a1b77]*/

View File

@ -1267,6 +1267,65 @@ PyLong_FromPy_off_t(Py_off_t offset)
#endif
}
#ifdef HAVE_SIGSET_T
/* Convert an iterable of integers to a sigset.
Return 1 on success, return 0 and raise an exception on error. */
int
_Py_Sigset_Converter(PyObject *obj, void *addr)
{
sigset_t *mask = (sigset_t *)addr;
PyObject *iterator, *item;
long signum;
int overflow;
if (sigemptyset(mask)) {
/* Probably only if mask == NULL. */
PyErr_SetFromErrno(PyExc_OSError);
return 0;
}
iterator = PyObject_GetIter(obj);
if (iterator == NULL) {
return 0;
}
while ((item = PyIter_Next(iterator)) != NULL) {
signum = PyLong_AsLongAndOverflow(item, &overflow);
Py_DECREF(item);
if (signum <= 0 || signum >= NSIG) {
if (overflow || signum != -1 || !PyErr_Occurred()) {
PyErr_Format(PyExc_ValueError,
"signal number %ld out of range", signum);
}
goto error;
}
if (sigaddset(mask, (int)signum)) {
if (errno != EINVAL) {
/* Probably impossible */
PyErr_SetFromErrno(PyExc_OSError);
goto error;
}
/* For backwards compatibility, allow idioms such as
* `range(1, NSIG)` but warn about invalid signal numbers
*/
const char msg[] =
"invalid signal number %ld, please use valid_signals()";
if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, msg, signum)) {
goto error;
}
}
}
if (!PyErr_Occurred()) {
Py_DECREF(iterator);
return 1;
}
error:
Py_DECREF(iterator);
return 0;
}
#endif /* HAVE_SIGSET_T */
#ifdef MS_WINDOWS
static int

View File

@ -17,8 +17,17 @@ PyAPI_FUNC(PyObject *) _PyLong_FromGid(gid_t);
PyAPI_FUNC(int) _Py_Uid_Converter(PyObject *, void *);
PyAPI_FUNC(int) _Py_Gid_Converter(PyObject *, void *);
#endif /* MS_WINDOWS */
#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGWAIT) || \
defined(HAVE_SIGWAITINFO) || defined(HAVE_SIGTIMEDWAIT)
# define HAVE_SIGSET_T
#endif
#ifdef HAVE_SIGSET_T
PyAPI_FUNC(int) _Py_Sigset_Converter(PyObject *, void *);
#endif /* HAVE_SIGSET_T */
#endif /* Py_LIMITED_API */
#ifdef __cplusplus
}
#endif

View File

@ -59,6 +59,14 @@ module signal
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=b0301a3bde5fe9d3]*/
/*[python input]
class sigset_t_converter(CConverter):
type = 'sigset_t'
converter = '_Py_Sigset_Converter'
[python start generated code]*/
/*[python end generated code: output=da39a3ee5e6b4b0d input=b5689d14466b6823]*/
/*
NOTES ON THE INTERACTION BETWEEN SIGNALS AND THREADS
@ -808,69 +816,6 @@ signal_getitimer_impl(PyObject *module, int which)
#endif
#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGWAIT) || \
defined(HAVE_SIGWAITINFO) || defined(HAVE_SIGTIMEDWAIT)
/* Convert an iterable to a sigset.
Return 0 on success, return -1 and raise an exception on error. */
static int
iterable_to_sigset(PyObject *iterable, sigset_t *mask)
{
int result = -1;
PyObject *iterator, *item;
long signum;
sigemptyset(mask);
iterator = PyObject_GetIter(iterable);
if (iterator == NULL)
goto error;
while (1)
{
item = PyIter_Next(iterator);
if (item == NULL) {
if (PyErr_Occurred())
goto error;
else
break;
}
signum = PyLong_AsLong(item);
Py_DECREF(item);
if (signum == -1 && PyErr_Occurred())
goto error;
if (0 < signum && signum < NSIG) {
if (sigaddset(mask, (int)signum)) {
if (errno != EINVAL) {
/* Probably impossible */
PyErr_SetFromErrno(PyExc_OSError);
goto error;
}
/* For backwards compatibility, allow idioms such as
* `range(1, NSIG)` but warn about invalid signal numbers
*/
const char *msg =
"invalid signal number %ld, please use valid_signals()";
if (PyErr_WarnFormat(PyExc_RuntimeWarning, 1, msg, signum)) {
goto error;
}
}
}
else {
PyErr_Format(PyExc_ValueError,
"signal number %ld out of range", signum);
goto error;
}
}
result = 0;
error:
Py_XDECREF(iterator);
return result;
}
#endif
#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGPENDING)
static PyObject*
sigset_to_set(sigset_t mask)
@ -913,23 +858,20 @@ sigset_to_set(sigset_t mask)
signal.pthread_sigmask
how: int
mask: object
mask: sigset_t
/
Fetch and/or change the signal mask of the calling thread.
[clinic start generated code]*/
static PyObject *
signal_pthread_sigmask_impl(PyObject *module, int how, PyObject *mask)
/*[clinic end generated code: output=ff640fe092bc9181 input=f3b7d7a61b7b8283]*/
signal_pthread_sigmask_impl(PyObject *module, int how, sigset_t mask)
/*[clinic end generated code: output=0562c0fb192981a8 input=85bcebda442fa77f]*/
{
sigset_t newmask, previous;
sigset_t previous;
int err;
if (iterable_to_sigset(mask, &newmask))
return NULL;
err = pthread_sigmask(how, &newmask, &previous);
err = pthread_sigmask(how, &mask, &previous);
if (err != 0) {
errno = err;
PyErr_SetFromErrno(PyExc_OSError);
@ -977,7 +919,7 @@ signal_sigpending_impl(PyObject *module)
/*[clinic input]
signal.sigwait
sigset: object
sigset: sigset_t
/
Wait for a signal.
@ -988,17 +930,13 @@ and returns the signal number.
[clinic start generated code]*/
static PyObject *
signal_sigwait(PyObject *module, PyObject *sigset)
/*[clinic end generated code: output=557173647424f6e4 input=11af2d82d83c2e94]*/
signal_sigwait_impl(PyObject *module, sigset_t sigset)
/*[clinic end generated code: output=f43770699d682f96 input=a6fbd47b1086d119]*/
{
sigset_t set;
int err, signum;
if (iterable_to_sigset(sigset, &set))
return NULL;
Py_BEGIN_ALLOW_THREADS
err = sigwait(&set, &signum);
err = sigwait(&sigset, &signum);
Py_END_ALLOW_THREADS
if (err) {
errno = err;
@ -1113,7 +1051,7 @@ fill_siginfo(siginfo_t *si)
/*[clinic input]
signal.sigwaitinfo
sigset: object
sigset: sigset_t
/
Wait synchronously until one of the signals in *sigset* is delivered.
@ -1122,20 +1060,16 @@ Returns a struct_siginfo containing information about the signal.
[clinic start generated code]*/
static PyObject *
signal_sigwaitinfo(PyObject *module, PyObject *sigset)
/*[clinic end generated code: output=c40f27b269cd2309 input=f3779a74a991e171]*/
signal_sigwaitinfo_impl(PyObject *module, sigset_t sigset)
/*[clinic end generated code: output=1eb2f1fa236fdbca input=3d1a7e1f27fc664c]*/
{
sigset_t set;
siginfo_t si;
int err;
int async_err = 0;
if (iterable_to_sigset(sigset, &set))
return NULL;
do {
Py_BEGIN_ALLOW_THREADS
err = sigwaitinfo(&set, &si);
err = sigwaitinfo(&sigset, &si);
Py_END_ALLOW_THREADS
} while (err == -1
&& errno == EINTR && !(async_err = PyErr_CheckSignals()));
@ -1152,7 +1086,7 @@ signal_sigwaitinfo(PyObject *module, PyObject *sigset)
/*[clinic input]
signal.sigtimedwait
sigset: object
sigset: sigset_t
timeout as timeout_obj: object
/
@ -1162,12 +1096,11 @@ The timeout is specified in seconds, with floating point numbers allowed.
[clinic start generated code]*/
static PyObject *
signal_sigtimedwait_impl(PyObject *module, PyObject *sigset,
signal_sigtimedwait_impl(PyObject *module, sigset_t sigset,
PyObject *timeout_obj)
/*[clinic end generated code: output=f7eff31e679f4312 input=53fd4ea3e3724eb8]*/
/*[clinic end generated code: output=59c8971e8ae18a64 input=87fd39237cf0b7ba]*/
{
struct timespec ts;
sigset_t set;
siginfo_t si;
int res;
_PyTime_t timeout, deadline, monotonic;
@ -1181,9 +1114,6 @@ signal_sigtimedwait_impl(PyObject *module, PyObject *sigset,
return NULL;
}
if (iterable_to_sigset(sigset, &set))
return NULL;
deadline = _PyTime_GetMonotonicClock() + timeout;
do {
@ -1191,7 +1121,7 @@ signal_sigtimedwait_impl(PyObject *module, PyObject *sigset,
return NULL;
Py_BEGIN_ALLOW_THREADS
res = sigtimedwait(&set, &si, &ts);
res = sigtimedwait(&sigset, &si, &ts);
Py_END_ALLOW_THREADS
if (res != -1)