[Python-Dev] Py_CLEAR to avoid crashes

Amaury Forgeot d'Arc amauryfa at gmail.com
Sun Feb 17 00:12:42 CET 2008


Hello,

I think I found a prolific vein of potential crashes throughout the
python interpreter.
The idea is inspired from the discussion Issue1020188 and is quite
simple: find a Py_DECREF(xxx->yyy), where xxx represents any kind of
Python object, and craft a __del__ method so that the same function is
recursively called with the same object.

I recently submitted corrections for 3 problems found this way. Here
are two more examples of this method:

#====================================
class Special:
    def __del__(self):
        print a.args

class MyException(BaseException):
    def __init__(self):
        global a
        a = self
        BaseException.__init__(self, Special(), 0)
        BaseException.__init__(self, "other", 1)

MyException() # segfault
#====================================
import sys
class Special2(str):
    def __del__(self):
        f.__init__("@temp", "w")

f = file(Special2("@temp"), "w")
f.__init__("@temp", "w")  # segfault
#====================================

Issue1020188 (which is still open btw) deals with member reset to
NULL; but any modification of the pointer is potentially concerned.

And the correction is always the same: use Py_CLEAR, or a similar
construction that detach the value from the structure before
defcref'ing it.

I think it would help a lot of code if we had a safe standard way to
set struct members from C. A macro like the following:
    Py_SETREF(lvalue, newobj)
(whatever its name) would perform the assignment and expand to code
equivalent to:
    PyObject* oldobj = lvalue;
    Py_INCREF(newobj);
    lvalue = newobj;
    Py_DECREF(oldobj);

I am currently searching for all places that could benefit of this,
but I am not sure to find a test case for every potential crash.
Most of the time, it is very unlikely that "normal" python code can
fall in these traps (who calls f.__init__ in a __del__ method?), with
the exception of the one corrected by r60871.

Should we however intensively search and correct all of them?
Is there a clever way to prevent these problems globally, for example
by delaying finalizers "just a little"?

-- 
Amaury Forgeot d'Arc


More information about the Python-Dev mailing list