Skip to content

Commit bf06241

Browse files
committed
gh-142781: Fix type confusion in zoneinfo weak cache
Validate the types returned from _weak_cache.get() and _weak_cache.setdefault() to prevent type confusion when a ZoneInfo subclass provides a misbehaving cache implementation.
1 parent 8b64dd8 commit bf06241

File tree

3 files changed

+48
-0
lines changed

3 files changed

+48
-0
lines changed

Lib/test/test_zoneinfo/test_zoneinfo.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,33 @@ class EvilZoneInfo(self.klass):
15751575
class CZoneInfoCacheTest(ZoneInfoCacheTest):
15761576
module = c_zoneinfo
15771577

1578+
def test_weak_cache_get_type_confusion(self):
1579+
class EvilCache:
1580+
def get(self, key, default=None):
1581+
return 1337
1582+
1583+
class EvilZoneInfo(self.klass):
1584+
pass
1585+
1586+
EvilZoneInfo._weak_cache = EvilCache()
1587+
1588+
with self.assertRaises(TypeError):
1589+
EvilZoneInfo("America/Los_Angeles")
1590+
1591+
def test_weak_cache_setdefault_type_confusion(self):
1592+
class EvilCache:
1593+
def get(self, key, default=None):
1594+
return default
1595+
def setdefault(self, key, value):
1596+
return 1337
1597+
1598+
class EvilZoneInfo(self.klass):
1599+
pass
1600+
1601+
EvilZoneInfo._weak_cache = EvilCache()
1602+
1603+
with self.assertRaises(TypeError):
1604+
EvilZoneInfo("America/Los_Angeles")
15781605

15791606
class ZoneInfoPickleTest(TzPathUserMixin, ZoneInfoTestBase):
15801607
module = py_zoneinfo
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a type confusion in zoneinfo where a malicious override of _weak_cache
2+
in a zoneinfo subclass can cause memory corruption and crash the
3+
interpreter.

Modules/_zoneinfo.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,15 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key)
327327
return NULL;
328328
}
329329

330+
if (instance != Py_None && !PyObject_TypeCheck(instance, type)) {
331+
const char *e = "%s._weak_cache.get() returned %s, expected %s";
332+
PyErr_Format(PyExc_TypeError, e,
333+
type->tp_name, Py_TYPE(instance)->tp_name, type->tp_name);
334+
Py_DECREF(instance);
335+
Py_DECREF(weak_cache);
336+
return NULL;
337+
}
338+
330339
if (instance == Py_None) {
331340
Py_DECREF(instance);
332341
PyObject *tmp = zoneinfo_new_instance(state, type, key);
@@ -342,6 +351,15 @@ zoneinfo_ZoneInfo_impl(PyTypeObject *type, PyObject *key)
342351
Py_DECREF(weak_cache);
343352
return NULL;
344353
}
354+
355+
if (!PyObject_TypeCheck(instance, type)) {
356+
const char *e = "%s._weak_cache.setdefault() returned %s, expected %s";
357+
PyErr_Format(PyExc_TypeError, e,
358+
type->tp_name, Py_TYPE(instance)->tp_name, type->tp_name);
359+
Py_DECREF(instance);
360+
Py_DECREF(weak_cache);
361+
return NULL;
362+
}
345363
((PyZoneInfo_ZoneInfo *)instance)->source = SOURCE_CACHE;
346364
}
347365

0 commit comments

Comments
 (0)