Skip to content

Commit cdf9d54

Browse files
author
Anselm Kruis
committed
merge 3.3-slp (Stackless python#117, fix various reference leaks)
2 parents dc3cfaf + 8450bb9 commit cdf9d54

6 files changed

Lines changed: 70 additions & 13 deletions

File tree

Python/ceval.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
12161216
f->f_execute = slp_eval_frame_noval;
12171217
return slp_eval_frame_value(f, throwflag, retval);
12181218
exit_eval_frame:
1219+
Py_XDECREF(retval);
12191220
Py_LeaveRecursiveCall();
12201221
f->f_executing = 0;
12211222
tstate->frame = f->f_back;

Stackless/changelog.txt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,16 @@ What's New in Stackless 3.X.X?
2020

2121
- https://bitbucket.org/stackless-dev/stackless/issues/117
2222
Fix various reference leaks:
23-
- Leak of a reference to Py_None in generator.throw()
24-
- Leak of a reference to the thread-id of every thread returned by stackless.threads
23+
- Leak of a reference to None in generator.throw()
24+
- Leak of a reference to the thread-id of every thread returned by
25+
stackless.threads
26+
- Leak of a reference to None (usually) in the C-implementations of
27+
channel.send_throw() and channel.send_exception()
28+
- Leak of a reference to the caught exception (type and value) and traceback,
29+
if Stackless invokes an user defined error-handler.
30+
- Leak to an object, if a Python profile or trace function fails.
31+
- Various leaks, if stackless._wrap.frame.__setstate__() fails, because its
32+
state argument is invalid.
2533

2634
Additionally this change brings the handling of caught exceptions more in
2735
line with C-Python.

Stackless/module/channelobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ channel_send_exception(PyObject *myself, PyObject *args)
664664
goto err_exit;
665665
}
666666
Py_INCREF(Py_None);
667-
retval = Py_None;
667+
Py_SETREF(retval, Py_None);
668668
err_exit:
669669
Py_DECREF(klass);
670670
Py_XDECREF(args);
@@ -734,7 +734,7 @@ channel_send_throw(PyObject *myself, PyObject *args)
734734
goto err_exit;
735735
}
736736
Py_INCREF(Py_None);
737-
retval = Py_None;
737+
Py_SETREF(retval, Py_None);
738738
err_exit:
739739
return retval;
740740
}

Stackless/module/stacklessmodule.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -807,12 +807,20 @@ PyStackless_CallErrorHandler(void)
807807
if (slp_error_handler != NULL) {
808808
PyObject *exc, *val, *tb;
809809
PyObject *result;
810-
PyErr_Fetch(&exc, &val, &tb);
811-
if (!val)
810+
PyErr_Fetch(&exc, &val, &tb); /* we own the refs in exc, val and tb */
811+
assert(exc != NULL);
812+
if (!val) {
813+
Py_INCREF(Py_None);
812814
val = Py_None;
813-
if (!tb)
815+
}
816+
if (!tb) {
817+
Py_INCREF(Py_None);
814818
tb = Py_None;
819+
}
815820
result = PyObject_CallFunction(slp_error_handler, "OOO", exc, val, tb);
821+
Py_DECREF(exc);
822+
Py_DECREF(val);
823+
Py_DECREF(tb);
816824
Py_XDECREF(result);
817825
if (!result)
818826
return -1; /* an error in the handler! */

Stackless/pickling/prickelpit.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,7 @@ frame_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
873873
if (globals == NULL)
874874
return NULL;
875875
f = PyFrame_New(ts, (PyCodeObject *) f_code, globals, globals);
876+
assert(f->f_execute == NULL); /* frame is not executable */
876877
if (f != NULL)
877878
Py_TYPE(f) = &wrap_PyFrame_Type;
878879
Py_DECREF(globals);
@@ -883,7 +884,7 @@ frame_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
883884
static PyObject *
884885
frame_setstate(PyFrameObject *f, PyObject *args)
885886
{
886-
int /*f_restricted, */ f_lasti, f_lineno, i;
887+
int f_lasti, f_lineno, i;
887888
PyObject *f_globals, *f_locals, *blockstack_as_tuple;
888889
PyObject *localsplus_as_tuple, *exc_as_tuple, *trace, *f_code;
889890
PyObject *exec_name = NULL;
@@ -893,6 +894,9 @@ frame_setstate(PyFrameObject *f, PyObject *args)
893894

894895
if (is_wrong_type(Py_TYPE(f))) return NULL;
895896

897+
Py_CLEAR(f->f_globals);
898+
Py_CLEAR(f->f_locals);
899+
896900
if (!PyArg_ParseTuple (args, frametuplesetstatefmt,
897901
&PyCode_Type, &f_code,
898902
&valid,
@@ -901,7 +905,6 @@ frame_setstate(PyFrameObject *f, PyObject *args)
901905
&have_locals,
902906
&PyDict_Type, &f_locals,
903907
&trace,
904-
/* &f_restricted, */
905908
&exc_as_tuple,
906909
&f_lasti,
907910
&f_lineno,
@@ -936,17 +939,21 @@ frame_setstate(PyFrameObject *f, PyObject *args)
936939
goto err_exit;
937940
}
938941
Py_INCREF(trace);
942+
assert(f->f_trace == NULL);
939943
f->f_trace = trace;
940944
}
941945

942-
/* f->f_restricted = f_restricted; */
943-
944946
if (exc_as_tuple != Py_None) {
945947
if (PyTuple_GET_SIZE(exc_as_tuple) != 4) {
946948
PyErr_SetString(PyExc_ValueError,
947949
"bad exception tuple for frame");
948950
goto err_exit;
949951
}
952+
assert(f->f_exc_type == NULL);
953+
assert(f->f_exc_value == NULL);
954+
assert(f->f_exc_traceback == NULL);
955+
assert(((&f->f_exc_type) + 1 == &f->f_exc_value) &&
956+
((&f->f_exc_type) + 2 == &f->f_exc_traceback));
950957
slp_from_tuple_with_nulls(&f->f_exc_type, exc_as_tuple);
951958
}
952959

@@ -1022,14 +1029,18 @@ frame_setstate(PyFrameObject *f, PyObject *args)
10221029
}
10231030
}
10241031

1025-
/* see if this frame is valid to be run */
1032+
/* See if this frame is valid to be run. */
10261033
f->f_execute = valid ? good_func : bad_func;
10271034

10281035
Py_TYPE(f) = &PyFrame_Type;
10291036
Py_INCREF(f);
10301037
return (PyObject *) f;
10311038
err_exit:
1032-
Py_XDECREF(f);
1039+
/* Make sure that the frame is not executable. */
1040+
f->f_execute = NULL;
1041+
/* Clear members that could leak. */
1042+
PyFrame_Type.tp_clear((PyObject*)f);
1043+
10331044
return NULL;
10341045
}
10351046

Stackless/unittests/test_pickle.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,35 @@ def func(current):
532532
self.assertIsInstance(cell, cell_type)
533533
self.assertIs(cell.cell_contents, result)
534534

535+
def testSetstateFailure(self):
536+
# incomplete, just one of many failure modes of stackless._wrap.frame.__setstate__
537+
foo = "foo"
538+
539+
def f1(bar="bar"):
540+
try:
541+
1 / 0
542+
except ZeroDivisionError:
543+
x = foo
544+
locals()
545+
return stackless._wrap.frame.__reduce__(sys._getframe())
546+
assert False
547+
548+
def f2():
549+
try:
550+
1 / 0
551+
except ZeroDivisionError:
552+
return f1()
553+
554+
r = f2()
555+
self.assertEqual(len(r), 3)
556+
wrap_frame = r[0](*r[1])
557+
self.assertIsInstance(wrap_frame, stackless._wrap.frame)
558+
invalid_state = r[2][:-2] + ((("Not a", "tuple of 3", "integers"),), r[2][-1])
559+
self.assertRaisesRegexp(TypeError, "an integer is required", wrap_frame.__setstate__, invalid_state)
560+
# must not raise an assertion
561+
wrap_frame.__setstate__(r[2])
562+
self.assertIs(type(wrap_frame), types.FrameType)
563+
535564

536565
class TestDictViewPickling(StacklessPickleTestCase):
537566

0 commit comments

Comments
 (0)