Skip to content
27 changes: 27 additions & 0 deletions Lib/test/test_zoneinfo/test_zoneinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,33 @@ class EvilZoneInfo(self.klass):
class CZoneInfoCacheTest(ZoneInfoCacheTest):
module = c_zoneinfo

def test_weak_cache_get_type_confusion(self):
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
class EvilCache:
def get(self, key, default=None):
return 1337

class EvilZoneInfo(self.klass):
pass

EvilZoneInfo._weak_cache = EvilCache()

with self.assertRaises(TypeError):
EvilZoneInfo("America/Los_Angeles")

def test_weak_cache_setdefault_type_confusion(self):
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
class EvilCache:
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
def get(self, key, default=None):
return default
def setdefault(self, key, value):
return 1337

class EvilZoneInfo(self.klass):
pass

EvilZoneInfo._weak_cache = EvilCache()

with self.assertRaises(TypeError):
EvilZoneInfo("America/Los_Angeles")

class ZoneInfoPickleTest(TzPathUserMixin, ZoneInfoTestBase):
module = py_zoneinfo
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a type confusion in zoneinfo where a malicious override of _weak_cache
in a zoneinfo subclass can cause memory corruption and crash the
interpreter.
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
18 changes: 18 additions & 0 deletions Modules/_zoneinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,15 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key)
return NULL;
}

if (instance != Py_None && !PyObject_TypeCheck(instance, type)) {
const char *e = "%s._weak_cache.get() returned %s, expected %s";
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
PyErr_Format(PyExc_TypeError, e,
type->tp_name, Py_TYPE(instance)->tp_name, type->tp_name);
Py_DECREF(instance);
Py_DECREF(weak_cache);
return NULL;
}

if (instance == Py_None) {
Py_DECREF(instance);
PyObject *tmp = zoneinfo_new_instance(state, type, key);
Expand All @@ -342,6 +351,15 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key)
Py_DECREF(weak_cache);
return NULL;
}

if (!PyObject_TypeCheck(instance, type)) {
const char *e = "%s._weak_cache.setdefault() returned %s, expected %s";
PyErr_Format(PyExc_TypeError, e,
type->tp_name, Py_TYPE(instance)->tp_name, type->tp_name);
Py_DECREF(instance);
Py_DECREF(weak_cache);
return NULL;
}
((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE;
Comment thread
superboy-zjc marked this conversation as resolved.
Outdated
}

Expand Down
Loading