Segfault in name mapper
Jon Siddle <[email protected]> Mon, 13 Sep 2010 12:00:06 +0100
| Newsgroups | gmane.comp.python.cheetah |
|---|---|
| Organization | Corefiling Ltd |
| Message-ID | <[email protected]> |
It is possible to trivially cause the name mapper to segfault if
getattr throws an exception:
$a.b.c
If a.__getattr__('b') throws an exception, the name mapper will
segfault. This is caused by the
following code in PyNamemapper_valueForName():
nextVal = PyObject_GetAttrString(currentVal, currentKey);
if (exc != NULL) {
// if exception == AttributeError
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
setNotFoundException(currentKey, currentVal);
if (i > 0) {
Py_DECREF(currentVal);
}
return NULL;
}
}
As you can see, only AttributeError is handled. Any other exception will
cause currentVal to be NULL
the next time round the loop and the call to
PyObject_GetAttrString(currentVal, currentKey) will segfault.
The solution is simply to move the decref-return code outside the check
for AttributeError so that it happens
for all exceptions.
The attached patch fixes the bug, and adds a test for it. I've added a
return code check to the buildandrun
script so the segfault doesn't go unnoticed when the test fails.
FYI We encountered this when using the sqlalchemy ORM, which can throw
exceptions from __getattr__ on
objects in the session, so it's quite likely that others will encounter
this.
Thanks
Jon
--
Jon Siddle, CoreFiling Limited
Software Tools Developer
http://www.corefiling.com
Phone: +44-1865-203192
------------------------------------------------------------------------------
Start uncovering the many advantages of virtual appliances
and start using them to simplify application deployment and
accelerate your shift to cloud computing
http://p.sf.net/sfu/novell-sfdev2dev
_______________________________________________
Cheetahtemplate-discuss mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/cheetahtemplate-discuss
getattr-segfault-fix.patch
(text/plain, 2.4 KB)
diff --git a/buildandrun b/buildandrun
index d3b4e42..41a636a 100755
--- a/buildandrun
+++ b/buildandrun
@@ -58,7 +58,8 @@ def main():
os.putenv('PYTHONPATH', libdir)
rc = subprocess.call( ['python',] + args )
-
+ if rc == -11:
+ logging.error('Segmentation fault in test process. Test failed.')
if __name__ == '__main__':
diff --git a/cheetah/Tests/NameMapper.py b/cheetah/Tests/NameMapper.py
index ac42244..c44fb4c 100644
--- a/cheetah/Tests/NameMapper.py
+++ b/cheetah/Tests/NameMapper.py
@@ -43,6 +43,10 @@ class DummyClass(object):
except:
raise
+class DummyClassGetAttrRaises(object):
+ def __getattr__(self, name):
+ raise ValueError
+
def dummyFunc(arg="Scooby"):
return arg
@@ -67,6 +71,7 @@ testNamespace = {
'aClass': DummyClass,
'aFunc': dummyFunc,
'anObj': DummyClass(),
+ 'anObjThatRaises': DummyClassGetAttrRaises(),
'aMeth': DummyClass().meth1,
'none': None,
'emptyString': '',
@@ -419,6 +424,14 @@ class VFN(NameMapperTest):
for i in range(10):
self.get('aDict.nestedDict.funcThatRaises', False)
+ def test61(self):
+ """Accessing attribute where __getattr__ raises shouldn't segfault if something follows it"""
+
+ def test(self=self):
+ self.get('anObjThatRaises.willraise.anything')
+ self.assertRaises(ValueError, test)
+
+
class VFS(VFN):
_searchListLength = 1
diff --git a/cheetah/c/_namemapper.c b/cheetah/c/_namemapper.c
index a41571b..a114658 100644
--- a/cheetah/c/_namemapper.c
+++ b/cheetah/c/_namemapper.c
@@ -188,14 +188,15 @@ static PyObject *PyNamemapper_valueForName(PyObject *obj, char *nameChunks[], in
nextVal = PyObject_GetAttrString(currentVal, currentKey);
exc = PyErr_Occurred();
if (exc != NULL) {
- // if exception == AttributeError
+ // if exception == AttributeError, report our own exception
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
setNotFoundException(currentKey, currentVal);
- if (i > 0) {
- Py_DECREF(currentVal);
- }
- return NULL;
}
+ // any exceptions results in failure
+ if (i > 0) {
+ Py_DECREF(currentVal);
+ }
+ return NULL;
}
}
if (i > 0) {