Skip to content

Commit 44c7230

Browse files
author
Anselm Kruis
committed
merge 3.4-slp (Stackless python#96, python#98)
2 parents 4717ee3 + 0125e06 commit 44c7230

File tree

4 files changed

+86
-6
lines changed

4 files changed

+86
-6
lines changed

Modules/_pickle.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -3706,8 +3706,14 @@ save(PicklerObject *self, PyObject *obj, int pers_save)
37063706

37073707
#ifdef STACKLESS
37083708
/* but we save the stack after a fixed watermark */
3709-
if (CSTACK_SAVE_NOW(PyThreadState_GET(), self))
3710-
return slp_safe_pickling((void *)&save, (PyObject *)self, obj, pers_save);
3709+
if (CSTACK_SAVE_NOW(PyThreadState_GET(), self)) {
3710+
int res;
3711+
if (Py_EnterRecursiveCall(" while pickling an object"))
3712+
return -1;
3713+
res = slp_safe_pickling((void *)&save, (PyObject *)self, obj, pers_save);
3714+
Py_LeaveRecursiveCall();
3715+
return res;
3716+
}
37113717
#endif
37123718
if (Py_EnterRecursiveCall(" while pickling an object"))
37133719
return -1;

Stackless/changelog.txt

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
Stackless-Python News
22
+++++++++++++++++++++
33

4-
This file documents changes to the Stackless extension of C-Python.
4+
This file documents user visible changes to the Stackless extension of C-Python.
55
For other changes see Misc/NEWS.
66

7-
87
What's New in Stackless 3.X.X?
98
==============================
109

1110
*Release date: 20XX-XX-XX*
1211

12+
- https://bitbucket.org/stackless-dev/stackless/issues/98
13+
Prevent an unlikely NULL-pointer access in safe_pickle.c pickle_callback().
14+
The bug is very old and might affect multiprocessing.
15+
16+
- https://bitbucket.org/stackless-dev/stackless/issues/96
17+
Impose a very high limit on the recursion depth of cPickle.
18+
Previously an infinite recursion could eat up all you memory
19+
and finally crash Stackless. No the recursion stops after using
20+
about 170MB (32bit) / 300MB (64bit) of memory.
21+
1322
- https://bitbucket.org/stackless-dev/stackless/issues/93
1423
Unify tasklet.kill(), tasklet.throw() and tasklet.raise_exception().
1524
They now behave almost identically. This affects the error handling in some

Stackless/pickling/safe_pickle.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ pickle_callback(PyFrameObject *f, int exc, PyObject *retval)
3131
*/
3232
saved_base = ts->st.cstack_root;
3333
ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f;
34-
Py_DECREF(retval);
35-
cf->i = cPickle_save(cf->ob1, cf->ob2, cf->n);
34+
if (retval) {
35+
Py_DECREF(retval);
36+
cf->i = cPickle_save(cf->ob1, cf->ob2, cf->n);
37+
} else {
38+
cf->i = -1;
39+
}
3640
ts->st.cstack_root = saved_base;
3741

3842
/* jump back. No decref, frame contains result. */
@@ -41,6 +45,7 @@ pickle_callback(PyFrameObject *f, int exc, PyObject *retval)
4145
ts->frame = cf->f_back;
4246
slp_transfer_return(cst);
4347
/* never come here */
48+
assert(0);
4449
return NULL;
4550
}
4651

Stackless/unittests/test_defects.py

+60
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import gc
55
import sys
66
import types
7+
from io import BytesIO
8+
import time
79
try:
810
import threading
911
withThreads = True
@@ -428,6 +430,64 @@ def test_invalid_args_tasklet_kill(self):
428430
self.assertRaises(TypeError, func, False, None)
429431

430432

433+
class TestCPickleBombHandling_Dict(dict):
434+
pass
435+
436+
437+
class TestCPickleBombHandling_Cls(object):
438+
def __getstate__(self):
439+
try:
440+
started = self.started
441+
except AttributeError:
442+
pass
443+
else:
444+
self.started = None
445+
started.set()
446+
# print("started")
447+
time.sleep(0.05) # give the other thread a chance to run
448+
return self.__dict__
449+
450+
451+
class TestCPickleBombHandling(StacklessTestCase):
452+
def other_thread(self, pickler, c):
453+
try:
454+
pickler.dump(c)
455+
except TaskletExit:
456+
self.killed = None
457+
except:
458+
self.killed = sys.exc_info()
459+
else:
460+
self.killed = False
461+
462+
def test_kill_during_cPickle_stack_switch(self):
463+
# this test kills the main/current tasklet of a other-thread,
464+
# which is fast-pickling a recursive structure. This leads to an
465+
# infinite recursion, which gets interrupted by a bomb thrown from
466+
# main-thread. Until issue #98 got fixed, this caused a crash.
467+
# See https://bitbucket.org/stackless-dev/stackless/issues/98
468+
buf = BytesIO()
469+
import _pickle as pickle
470+
pickler = pickle.Pickler(buf, protocol=-1)
471+
pickler.fast = 1
472+
473+
started = threading.Event()
474+
475+
c = TestCPickleBombHandling_Cls()
476+
c.started = started
477+
d = TestCPickleBombHandling_Dict()
478+
d[1] = d
479+
c.recursive = d
480+
self.killed = "undefined"
481+
t = threading.Thread(target=self.other_thread, name="other_thread", args=(pickler, c))
482+
t.start()
483+
started.wait()
484+
stackless.get_thread_info(t.ident)[0].kill(pending=True)
485+
# print("killing")
486+
t.join()
487+
if isinstance(self.killed, tuple):
488+
raise (self.killed[0], self.killed[1], self.killed[2])
489+
self.assertIsNone(self.killed)
490+
431491
if __name__ == '__main__':
432492
if not sys.argv[1:]:
433493
sys.argv.append('-v')

0 commit comments

Comments
 (0)