[Python-bugs-list] [ python-Bugs-483469 ] crash on unbounded recursion in __del__ .

noreply@sourceforge.net noreply@sourceforge.net
Tue, 27 Nov 2001 14:37:19 -0800


Bugs item #483469, was opened at 2001-11-19 07:49
You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=105470&aid=483469&group_id=5470

Category: Python Interpreter Core
Group: Python 2.1.1
Status: Open
Resolution: None
Priority: 4
Submitted By: te (elaias)
>Assigned to: Nobody/Anonymous (nobody)
Summary: crash on unbounded recursion in __del__ .

Initial Comment:
do the following.

>>> class C:
...     def __del__(self):
...         c = C()
>>> c = C()
>>> d = range(100) #anything really
>>> c = d
>>> c

Segmentation fault (core dumped)



----------------------------------------------------------------------

>Comment By: Tim Peters (tim_one)
Date: 2001-11-27 14:37

Message:
Logged In: YES 
user_id=31435

OK, Python's recursion check does trigger if "del c" is 
included at the end of the __del__ method.  However, as-is, 
the destruction of c is a side effect of __del__'s frame 
getting decref'ed, and the latter happens at the end of 
PyEval_EvalCodeEx().  eval_frame() is long gone by then, 
and tstate->recursion_depth is checked in the latter.

To be clear, there is a deep chain of Python frames, but 
they're all in the process of being destructed, and tstate-
>recursion_depth has already been decremented (it's just 3 
at the time this blew up on my Windows box):  eval_frame() 
is not on the C stack (except for a few times at the base 
of the C stack).

So, in this sense, it's a failure of the recursion-depth 
hacks to model the true depth of the C stack.  Note there 
are actually 10 levels of C stack for each Python level 
here:  PyEval_EvalCodeEx decrefs the frame, which invokes 
_Py_Dealloc, then to frame_dealloc, which decrefs c and 
triggers _Py_Dealloc -> instance_dealloc, which finds a 
__del__ method and so does PyEval_CallObjectWithKeywords -> 
PyObject_Call -> instancemethod_call -> PyObject_Call -> 
function_call -> PyEval_EvalCodeEx, and we start all over 
again.

No bright ideas here, so unassigned again.

----------------------------------------------------------------------

Comment By: Tim Peters (tim_one)
Date: 2001-11-27 13:00

Message:
Logged In: YES 
user_id=31435

Assigned to me (to figure out why the Python-level 
recursion check isn't triggering, not to tumble into the 
bottomless "outguess all C runtimes" pit).

----------------------------------------------------------------------

Comment By: Guido van Rossum (gvanrossum)
Date: 2001-11-23 18:17

Message:
Logged In: YES 
user_id=6380

Let's be clear about rexec.  It is currently a pathetic
excuse for security.  It *could* be made safe, and this is
one of the things that would have to be fixed; but there are
so many other places that I don't really care any more
whether this particular core dump gets fixed (at least not
from a security p.o.v.).

It's strange though that even though there's a Python
function at each level, the Python stack limit check doesn't
trigger before C stack overflows. Maybe it would make more
sense to spend some time getting the general stack limit
check more robust. Unfortunately this has to be done
separately for each C compiler and each operating system
variation... :-(

----------------------------------------------------------------------

Comment By: Andrew Bennetts (spiv)
Date: 2001-11-20 06:53

Message:
Logged In: YES 
user_id=50945

I agree that segfaults like this are a problem; this is a
potential security problem because it means that untrusted
code can crash the interpreter, even if you try to use
something like rexec.

----------------------------------------------------------------------

Comment By: te (elaias)
Date: 2001-11-19 09:45

Message:
Logged In: YES 
user_id=109241

Sorry, I thought that this was obvious enough for the person
responsible that I didn't have to explain. Thanks for the
correction though. And I would say that a segfault in any
program is a problem, especially an interpreter.

----------------------------------------------------------------------

Comment By: Tim Peters (tim_one)
Date: 2001-11-19 09:18

Message:
Logged In: YES 
user_id=31435

Well, it's not just "an assignment":  the __del__ creates a 
local instance of C, which is destroyed while __del__ is 
trying to return, which calls __del__ again, which creates 
a local instance of C, which is destroyed while __del__ is 
trying to return, which calls __del__ again, etc etc etc.  
So it's roughly the same as

class C:
    def __str__(self):
        return str(self)

print C()

You get unbounded recursion and eventually the stack blows 
up.  "Don't do that" is the best advice <wink>.  Reduced 
priority accordingly; *maybe* Python can be changed to 
detect the stack overflow and give a "maximum recursion 
depth" exception instead.

----------------------------------------------------------------------

You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=105470&aid=483469&group_id=5470