Skip to content

Commit 233e873

Browse files
committed
Stackless issue python#265: fix __reduce__ / __reduce_ex__
Avoid undefined behavior, if Stackless classes implement __reduce__() and __reduce_ex__() using a single C-function. __reduce_ex__ now checks the type of its argument.
1 parent 235fc6f commit 233e873

6 files changed

Lines changed: 46 additions & 12 deletions

File tree

Stackless/changelog.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ What's New in Stackless 3.X.X?
99

1010
*Release date: 20XX-XX-XX*
1111

12+
- https://github.com/stackless-dev/stackless/issues/265
13+
Avoid undefined behavior, if Stackless classes implement __reduce__() and
14+
__reduce_ex__() using a single C-function.
15+
1216
- https://github.com/stackless-dev/stackless/issues/265
1317
Fix C-API functions PyChannel_Send(), PyChannel_Send_nr(),
1418
PyChannel_Receive(), PyChannel_Receive_nr(), and

Stackless/core/cframeobject.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,19 @@ static PyObject * execute_soft_switchable_func(PyFrameObject *, int, PyObject *)
133133
SLP_DEF_INVALID_EXEC(execute_soft_switchable_func)
134134

135135
static PyObject *
136-
cframe_reduce(PyCFrameObject *cf)
136+
cframe_reduce(PyCFrameObject *cf, PyObject *value)
137137
{
138138
PyObject *res = NULL, *exec_name = NULL;
139139
PyObject *params = NULL;
140140
int valid = 1;
141141
PyObject *obs[3];
142142
long i, n;
143143

144+
if (value && !PyLong_Check(value)) {
145+
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
146+
return NULL;
147+
}
148+
144149
if (cf->f_execute == execute_soft_switchable_func) {
145150
exec_name = (PyObject *) cf->any2;
146151
assert(cf->any2);
@@ -240,7 +245,7 @@ cframe_setstate(PyObject *self, PyObject *args)
240245

241246
static PyMethodDef cframe_methods[] = {
242247
{"__reduce__", (PyCFunction)cframe_reduce, METH_NOARGS, NULL},
243-
{"__reduce_ex__", (PyCFunction)cframe_reduce, METH_VARARGS, NULL},
248+
{"__reduce_ex__", (PyCFunction)cframe_reduce, METH_O, NULL},
244249
{"__setstate__", (PyCFunction)cframe_setstate, METH_O, NULL},
245250
{NULL, NULL}
246251
};
@@ -386,8 +391,13 @@ slp_cframe_fini(void)
386391
*/
387392

388393
static PyObject *
389-
function_declaration_reduce(PyStacklessFunctionDeclarationObject *self)
394+
function_declaration_reduce(PyStacklessFunctionDeclarationObject *self, PyObject *value)
390395
{
396+
if (value && !PyLong_Check(value)) {
397+
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
398+
return NULL;
399+
}
400+
391401
if (self->name == NULL || *self->name == '\0') {
392402
PyErr_SetString(PyExc_SystemError, "no function name");
393403
return NULL;
@@ -397,7 +407,7 @@ function_declaration_reduce(PyStacklessFunctionDeclarationObject *self)
397407

398408
static PyMethodDef function_declaration_methods[] = {
399409
{"__reduce__", (PyCFunction)function_declaration_reduce, METH_NOARGS, NULL},
400-
{"__reduce_ex__", (PyCFunction)function_declaration_reduce, METH_VARARGS, NULL},
410+
{"__reduce_ex__", (PyCFunction)function_declaration_reduce, METH_O, NULL},
401411
{NULL, NULL}
402412
};
403413

Stackless/module/channelobject.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,12 +1104,17 @@ PyDoc_STRVAR(channel_reduce__doc__,
11041104
"channel.__reduce__() -- currently does not distinguish threads.");
11051105

11061106
static PyObject *
1107-
channel_reduce(PyChannelObject * ch)
1107+
channel_reduce(PyChannelObject * ch, PyObject *value)
11081108
{
11091109
PyObject *tup = NULL, *lis = NULL;
11101110
PyTaskletObject *t;
11111111
int i, n;
11121112

1113+
if (value && !PyLong_Check(value)) {
1114+
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
1115+
return NULL;
1116+
}
1117+
11131118
lis = PyList_New(0);
11141119
if (lis == NULL) goto err_exit;
11151120
t = ch->head;
@@ -1188,7 +1193,7 @@ channel_methods[] = {
11881193
channel_open__doc__},
11891194
{"__reduce__", (PCF)channel_reduce, METH_NOARGS,
11901195
channel_reduce__doc__},
1191-
{"__reduce_ex__", (PCF)channel_reduce, METH_VARARGS,
1196+
{"__reduce_ex__", (PCF)channel_reduce, METH_O,
11921197
channel_reduce__doc__},
11931198
{"__setstate__", (PCF)channel_setstate, METH_O,
11941199
channel_setstate__doc__},

Stackless/module/scheduling.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,15 @@ slp_bomb_explode(PyObject *_bomb)
216216
}
217217

218218
static PyObject *
219-
bomb_reduce(PyBombObject *bomb)
219+
bomb_reduce(PyBombObject *bomb, PyObject *value)
220220
{
221221
PyObject *tup;
222222

223+
if (value && !PyLong_Check(value)) {
224+
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
225+
return NULL;
226+
}
227+
223228
tup = slp_into_tuple_with_nulls(&bomb->curexc_type, 3);
224229
if (tup != NULL)
225230
tup = Py_BuildValue("(O()O)", &PyBomb_Type, tup);
@@ -246,7 +251,7 @@ static PyMemberDef bomb_members[] = {
246251

247252
static PyMethodDef bomb_methods[] = {
248253
{"__reduce__", (PyCFunction)bomb_reduce, METH_NOARGS},
249-
{"__reduce_ex__", (PyCFunction)bomb_reduce, METH_VARARGS},
254+
{"__reduce_ex__", (PyCFunction)bomb_reduce, METH_O},
250255
{"__setstate__", (PyCFunction)bomb_setstate, METH_O},
251256
{NULL, NULL} /* sentinel */
252257
};

Stackless/module/stacklessmodule.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -855,8 +855,13 @@ get_thread_info(PyObject *self, PyObject *args)
855855
}
856856

857857
static PyObject *
858-
slpmodule_reduce(PyObject *self)
858+
slpmodule_reduce(PyObject *self, PyObject *value)
859859
{
860+
if (value && !PyLong_Check(value)) {
861+
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
862+
return NULL;
863+
}
864+
860865
return PyUnicode_FromString("_stackless");
861866
}
862867

@@ -1709,7 +1714,7 @@ static PyMethodDef stackless_methods[] = {
17091714
_get_all_objects__doc__},
17101715
#endif
17111716
{"__reduce__", (PCF)slpmodule_reduce, METH_NOARGS, NULL},
1712-
{"__reduce_ex__", (PCF)slpmodule_reduce, METH_VARARGS, NULL},
1717+
{"__reduce_ex__", (PCF)slpmodule_reduce, METH_O, NULL},
17131718
{"getdebug", (PCF)slpmodule_getdebug, METH_NOARGS,
17141719
slpmodule_getdebug__doc__},
17151720
{"getuncollectables", (PCF)slpmodule_getuncollectables, METH_NOARGS,

Stackless/module/taskletobject.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,14 +612,19 @@ simply the tasklet() call without parameters.
612612
*/
613613

614614
static PyObject *
615-
tasklet_reduce(PyTaskletObject * t)
615+
tasklet_reduce(PyTaskletObject * t, PyObject *value)
616616
{
617617
PyObject *tup = NULL, *lis = NULL;
618618
PyFrameObject *f;
619619
PyThreadState *ts = t->cstate->tstate;
620620
PyObject *exc_type, *exc_value, *exc_traceback, *exc_info;
621621
PyObject *context = NULL;
622622

623+
if (value && !PyLong_Check(value)) {
624+
PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer");
625+
return NULL;
626+
}
627+
623628
if (ts && t == ts->st.current)
624629
RUNTIME_ERROR("You cannot __reduce__ the tasklet which is"
625630
" current.", NULL);
@@ -2580,7 +2585,7 @@ static PyMethodDef tasklet_methods[] = {
25802585
tasklet_setup__doc__},
25812586
{"__reduce__", (PCF)tasklet_reduce, METH_NOARGS,
25822587
tasklet_reduce__doc__},
2583-
{"__reduce_ex__", (PCF)tasklet_reduce, METH_VARARGS,
2588+
{"__reduce_ex__", (PCF)tasklet_reduce, METH_O,
25842589
tasklet_reduce__doc__},
25852590
{"__setstate__", (PCF)tasklet_setstate, METH_O,
25862591
tasklet_setstate__doc__},

0 commit comments

Comments
 (0)