Skip to content

Commit f2607c9

Browse files
author
Anselm Kruis
committed
Stackless issue python#98: Prevent an unlikely NULL-pointer access in safe_pickle.c pickle_callback().
The bug is very old and might affect multiprocessing. The change fixes the bug and adds a test case. https://bitbucket.org/stackless-dev/stackless/issues/98 (grafted from 7cd2a4e39aa757427ccbf176ef7a0cd6f8f1514e)
1 parent 9555348 commit f2607c9

File tree

3 files changed

+71
-2
lines changed

3 files changed

+71
-2
lines changed

Stackless/changelog.txt

+4
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://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+
1216
- https://bitbucket.org/stackless-dev/stackless/issues/96
1317
Impose a very high limit on the recursion depth of cPickle.
1418
Previously an infinite recursion could eat up all you memory

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)