[Python-checkins] r70797 - in python/branches/py3k: Doc/c-api/exceptions.rst Lib/test/test_exceptions.py Misc/NEWS Objects/exceptions.c

georg.brandl python-checkins at python.org
Tue Mar 31 06:16:11 CEST 2009


Author: georg.brandl
Date: Tue Mar 31 06:16:10 2009
New Revision: 70797

Log:
Fix segfaults when running test_exceptions with coverage tracing, caused by wrongly defining Exception.__context__ as a T_OBJECT structmember which does not set the member to NULL on None assignment, and generally does not do type checks. This could be used to crash the interpreter by setting any object to __context__. The same applies to __cause__.  Also document the PyException_* functions.

Modified:
   python/branches/py3k/Doc/c-api/exceptions.rst
   python/branches/py3k/Lib/test/test_exceptions.py
   python/branches/py3k/Misc/NEWS
   python/branches/py3k/Objects/exceptions.c

Modified: python/branches/py3k/Doc/c-api/exceptions.rst
==============================================================================
--- python/branches/py3k/Doc/c-api/exceptions.rst	(original)
+++ python/branches/py3k/Doc/c-api/exceptions.rst	Tue Mar 31 06:16:10 2009
@@ -400,6 +400,52 @@
    the warning message.
 
 
+Exception Objects
+=================
+
+.. cfunction:: PyObject* PyException_GetTraceback(PyObject *ex)
+
+   Return the traceback associated with the exception as a new reference, as
+   accessible from Python through :attr:`__traceback__`.  If there is no
+   traceback associated, this returns *NULL*.
+
+
+.. cfunction:: int PyException_SetTraceback(PyObject *ex, PyObject *tb)
+
+   Set the traceback associated with the exception to *tb*.  Use ``Py_None`` to
+   clear it.
+
+
+.. cfunction:: PyObject* PyException_GetContext(PyObject *ex)
+
+   Return the context (another exception instance during whose handling *ex* was
+   raised) associated with the exception as a new reference, as accessible from
+   Python through :attr:`__context__`.  If there is no context associated, this
+   returns *NULL*.
+
+
+.. cfunction:: void PyException_SetContext(PyObject *ex, PyObject *ctx)
+
+   Set the context associated with the exception to *ctx*.  Use *NULL* to clear
+   it.  There is no type check to make sure that *ctx* is an exception instance.
+   This steals a reference to *ctx*.
+
+
+.. cfunction:: PyObject* PyException_GetCause(PyObject *ex)
+
+   Return the cause (another exception instance set by ``raise ... from ...``)
+   associated with the exception as a new reference, as accessible from Python
+   through :attr:`__cause__`.  If there is no cause associated, this returns
+   *NULL*.
+
+
+.. cfunction:: void PyException_SetCause(PyObject *ex, PyObject *ctx)
+
+   Set the cause associated with the exception to *ctx*.  Use *NULL* to clear
+   it.  There is no type check to make sure that *ctx* is an exception instance.
+   This steals a reference to *ctx*.
+
+
 .. _standardexceptions:
 
 Standard Exceptions

Modified: python/branches/py3k/Lib/test/test_exceptions.py
==============================================================================
--- python/branches/py3k/Lib/test/test_exceptions.py	(original)
+++ python/branches/py3k/Lib/test/test_exceptions.py	Tue Mar 31 06:16:10 2009
@@ -341,6 +341,12 @@
         else:
             self.fail("No exception raised")
 
+    def testInvalidAttrs(self):
+        self.assertRaises(TypeError, setattr, Exception(), '__cause__', 1)
+        self.assertRaises(TypeError, delattr, Exception(), '__cause__')
+        self.assertRaises(TypeError, setattr, Exception(), '__context__', 1)
+        self.assertRaises(TypeError, delattr, Exception(), '__context__')
+
     def testNoneClearsTracebackAttr(self):
         try:
             raise IndexError(4)

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Tue Mar 31 06:16:10 2009
@@ -1,4 +1,5 @@
-+++++++++++ Python News
++++++++++++
+Python News
 +++++++++++
 
 (editors: check NEWS.help for information about editing NEWS using ReST.)
@@ -11,6 +12,9 @@
 Core and Builtins
 -----------------
 
+- Fix a segfault when running test_exceptions with coverage, caused by
+  insufficient checks in accessors of Exception.__context__.
+
 - Issue #5604: non-ASCII characters in module name passed to
   imp.find_module() were converted to UTF-8 while the path is
   converted to the default filesystem encoding, causing nonsense.

Modified: python/branches/py3k/Objects/exceptions.c
==============================================================================
--- python/branches/py3k/Objects/exceptions.c	(original)
+++ python/branches/py3k/Objects/exceptions.c	Tue Mar 31 06:16:10 2009
@@ -250,11 +250,67 @@
     return 0;
 }
 
+static PyObject *
+BaseException_get_context(PyObject *self) {
+    PyObject *res = PyException_GetContext(self);
+    if (res) return res;  /* new reference already returned above */
+    Py_RETURN_NONE;
+}
+
+static int
+BaseException_set_context(PyObject *self, PyObject *arg) {
+    if (arg == NULL) {
+        PyErr_SetString(PyExc_TypeError, "__context__ may not be deleted");
+        return -1;
+    } else if (arg == Py_None) {
+        arg = NULL;
+    } else if (!PyExceptionInstance_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "exception context must be None "
+                        "or derive from BaseException");
+        return -1;
+    } else {
+        /* PyException_SetContext steals this reference */
+        Py_INCREF(arg);
+    } 
+    PyException_SetContext(self, arg);
+    return 0;
+}
+
+static PyObject *
+BaseException_get_cause(PyObject *self) {
+    PyObject *res = PyException_GetCause(self);
+    if (res) return res;  /* new reference already returned above */
+    Py_RETURN_NONE;
+}
+
+static int
+BaseException_set_cause(PyObject *self, PyObject *arg) {
+    if (arg == NULL) {
+        PyErr_SetString(PyExc_TypeError, "__cause__ may not be deleted");
+        return -1;
+    } else if (arg == Py_None) {
+        arg = NULL;
+    } else if (!PyExceptionInstance_Check(arg)) {
+        PyErr_SetString(PyExc_TypeError, "exception cause must be None "
+                        "or derive from BaseException");
+        return -1;
+    } else {
+        /* PyException_SetCause steals this reference */
+        Py_INCREF(arg);
+    } 
+    PyException_SetCause(self, arg);
+    return 0;
+}
+
 
 static PyGetSetDef BaseException_getset[] = {
     {"__dict__", (getter)BaseException_get_dict, (setter)BaseException_set_dict},
     {"args", (getter)BaseException_get_args, (setter)BaseException_set_args},
     {"__traceback__", (getter)BaseException_get_tb, (setter)BaseException_set_tb},
+    {"__context__", (getter)BaseException_get_context,
+     (setter)BaseException_set_context, PyDoc_STR("exception context")},
+    {"__cause__", (getter)BaseException_get_cause,
+     (setter)BaseException_set_cause, PyDoc_STR("exception cause")},
     {NULL},
 };
 
@@ -303,14 +359,6 @@
 }
 
 
-static PyMemberDef BaseException_members[] = {
-    {"__context__", T_OBJECT, offsetof(PyBaseExceptionObject, context), 0,
-        PyDoc_STR("exception context")},
-    {"__cause__", T_OBJECT, offsetof(PyBaseExceptionObject, cause), 0,
-        PyDoc_STR("exception cause")},
-    {NULL}  /* Sentinel */
-};
-
 static PyTypeObject _PyExc_BaseException = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "BaseException", /*tp_name*/
@@ -341,7 +389,7 @@
     0,                          /* tp_iter */
     0,                          /* tp_iternext */
     BaseException_methods,      /* tp_methods */
-    BaseException_members,      /* tp_members */
+    0,                          /* tp_members */
     BaseException_getset,       /* tp_getset */
     0,                          /* tp_base */
     0,                          /* tp_dict */


More information about the Python-checkins mailing list