diff --git a/CHANGES.rst b/CHANGES.rst
index e40bd590ed3564bd27f47a42f5c6b9701b0f001e..7bd2ad05f823f96492952e9d7632116cac304a70 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -11,6 +11,8 @@ Features added
 Bugs fixed
 ----------
 
+* During final interpreter cleanup (with types cleanup enabled at compile time), extension types that inherit from base types over more than one level that were cimported from other modules could lead to a crash.
+
 * In CPython 3.3, converting a Unicode character to the Py_UNICODE type could fail to raise an overflow for non-BMP characters that do not fit into a wchar_t on the current platform.
 
 * Negative C integer constants lost their longness suffix in the generated C code.
diff --git a/Cython/Utility/ExtensionTypes.c b/Cython/Utility/ExtensionTypes.c
index 1e06ba13c9bd5f16907852df450dd4ba3426638e..423ed9673259123a9d16b5c55708e02d03bd4ebb 100644
--- a/Cython/Utility/ExtensionTypes.c
+++ b/Cython/Utility/ExtensionTypes.c
@@ -7,10 +7,13 @@ static void __Pyx_call_next_tp_dealloc(PyObject* obj, destructor current_tp_deal
 
 static void __Pyx_call_next_tp_dealloc(PyObject* obj, destructor current_tp_dealloc) {
     PyTypeObject* type = Py_TYPE(obj);
+    /* try to find the first parent type that has a different tp_dealloc() function */
     while (type && type->tp_dealloc != current_tp_dealloc)
         type = type->tp_base;
-    if (type && type->tp_base)
-        type->tp_base->tp_dealloc(obj);
+    while (type && type->tp_dealloc == current_tp_dealloc)
+        type = type->tp_base;
+    if (type)
+        type->tp_dealloc(obj);
 }
 
 /////////////// CallNextTpTraverse.proto ///////////////
@@ -21,10 +24,13 @@ static int __Pyx_call_next_tp_traverse(PyObject* obj, visitproc v, void *a, trav
 
 static int __Pyx_call_next_tp_traverse(PyObject* obj, visitproc v, void *a, traverseproc current_tp_traverse) {
     PyTypeObject* type = Py_TYPE(obj);
+    /* try to find the first parent type that has a different tp_traverse() function */
     while (type && type->tp_traverse != current_tp_traverse)
         type = type->tp_base;
-    if (type && type->tp_base && type->tp_base->tp_traverse)
-        return type->tp_base->tp_traverse(obj, v, a);
+    while (type && type->tp_traverse == current_tp_traverse)
+        type = type->tp_base;
+    if (type && type->tp_traverse)
+        return type->tp_traverse(obj, v, a);
     // FIXME: really ignore?
     return 0;
 }
@@ -37,8 +43,11 @@ static void __Pyx_call_next_tp_clear(PyObject* obj, inquiry current_tp_dealloc);
 
 static void __Pyx_call_next_tp_clear(PyObject* obj, inquiry current_tp_clear) {
     PyTypeObject* type = Py_TYPE(obj);
+    /* try to find the first parent type that has a different tp_clear() function */
     while (type && type->tp_clear != current_tp_clear)
         type = type->tp_base;
-    if (type && type->tp_base && type->tp_base->tp_clear)
-        type->tp_base->tp_clear(obj);
+    while (type && type->tp_clear == current_tp_clear)
+        type = type->tp_base;
+    if (type && type->tp_clear)
+        type->tp_clear(obj);
 }