Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.

Commit 74f33db

Browse files
author
Anselm Kruis
committed
Stackless issue #269: Fix frame_setstate() error handling
A failure to unpickle a frame could cause a NULL pointer access when deallocating the frame. This has been fixed.
1 parent e242275 commit 74f33db

File tree

3 files changed

+39
-11
lines changed

3 files changed

+39
-11
lines changed

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/269
13+
A failure to unpickle a frame could cause a NULL pointer access when
14+
deallocating the frame. This has been fixed.
15+
1216
- https://github.com/stackless-dev/stackless/issues/265
1317
Fix (invalid) function cast warnings with gcc 8 for method conventions
1418
different from METH_NOARGS, METH_O and METH_VARARGS excluding Argument Clinic

Stackless/pickling/prickelpit.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -934,8 +934,8 @@ frameobject_reduce(PyFrameObject *f, PyObject *unused)
934934
return res;
935935
}
936936

937-
#define frametuplenewfmt "O!"
938-
#define frametuplesetstatefmt "O!iUO!iO!OiiO!O:frame_new"
937+
#define frametuplenewfmt "O!:frame.__new__"
938+
#define frametuplesetstatefmt "O!iUO!iO!OiiO!O:frame.__setstate__"
939939

940940
static PyObject *
941941
frame_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
@@ -980,7 +980,6 @@ frame_setstate(PyFrameObject *f, PyObject *args)
980980

981981
if (is_wrong_type(Py_TYPE(f))) return NULL;
982982

983-
Py_CLEAR(f->f_globals);
984983
Py_CLEAR(f->f_locals);
985984

986985
if (!PyArg_ParseTuple (args, frametuplesetstatefmt,
@@ -1007,15 +1006,12 @@ frame_setstate(PyFrameObject *f, PyObject *args)
10071006
&bad_func))
10081007
return NULL;
10091008

1010-
Py_CLEAR(f->f_locals);
1011-
Py_CLEAR(f->f_globals);
1012-
10131009
if (have_locals) {
10141010
Py_INCREF(f_locals);
10151011
f->f_locals = f_locals;
10161012
}
10171013
Py_INCREF(f_globals);
1018-
f->f_globals = f_globals;
1014+
Py_SETREF(f->f_globals, f_globals);
10191015

10201016
if (trace != Py_None) {
10211017
if (!PyCallable_Check(trace)) {

Stackless/unittests/test_pickle.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,7 @@ def func(current):
546546
self.assertIsInstance(cell, cell_type)
547547
self.assertIs(cell.cell_contents, result)
548548

549-
def testSetstateFailure(self):
550-
# incomplete, just one of many failure modes of stackless._stackless._wrap.frame.__setstate__
549+
def _test_setstate_prepare(self):
551550
foo = "foo"
552551

553552
def f1(bar="bar"):
@@ -567,14 +566,43 @@ def f2():
567566

568567
r = f2()
569568
self.assertEqual(len(r), 3)
569+
570+
# r is a tuple (frame_type, (f_code), (state))
571+
# state is a tuple of the form
572+
# ('f_code', 'valid', 'exec_name', 'f_globals', 'have_locals',
573+
# 'f_locals', 'f_trace', 'f_lasti', 'f_lineno',
574+
# 'blockstack_as_tuple', 'localsplus_as_tuple')
575+
return r
576+
577+
def test_setstate_OK(self):
578+
r = self._test_setstate_prepare()
570579
wrap_frame = r[0](*r[1])
571580
self.assertIsInstance(wrap_frame, stackless._stackless._wrap.frame)
572-
invalid_state = r[2][:-2] + ((("Not a", "tuple of 3", "integers"),), r[2][-1])
573-
self.assertRaisesRegex(TypeError, "an integer is required", wrap_frame.__setstate__, invalid_state)
574581
# must not raise an assertion
575582
wrap_frame.__setstate__(r[2])
576583
self.assertIs(type(wrap_frame), types.FrameType)
577584

585+
def test_setstate_invalid_blockstack(self):
586+
r = self._test_setstate_prepare()
587+
# incomplete, just one of many failure modes of stackless._stackless._wrap.frame.__setstate__
588+
wrap_frame = r[0](*r[1])
589+
invalid_state = r[2][:-2] + ((("Not a", "tuple of 3", "integers"),), r[2][-1])
590+
self.assertRaisesRegex(TypeError, "an integer is required", wrap_frame.__setstate__, invalid_state)
591+
592+
def test_setstate_invalid_state(self):
593+
r = self._test_setstate_prepare()
594+
# incomplete, just one of many failure modes of stackless._stackless._wrap.frame.__setstate__
595+
wrap_frame = r[0](*r[1])
596+
invalid_state = ('completely', 'wrong')
597+
self.assertRaisesRegex(TypeError, "takes exactly 11 arguments", wrap_frame.__setstate__, invalid_state)
598+
599+
def test_setstate_different_code(self):
600+
r = self._test_setstate_prepare()
601+
# incomplete, just one of many failure modes of stackless._stackless._wrap.frame.__setstate__
602+
wrap_frame = r[0]((lambda:None).__code__)
603+
invalid_state = r[2]
604+
self.assertRaisesRegex(TypeError, "invalid code object for frame_setstate", wrap_frame.__setstate__, invalid_state)
605+
578606

579607
class TestDictViewPickling(StacklessPickleTestCase):
580608

0 commit comments

Comments
 (0)