-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
gh-142781: Fix type confusion in zoneinfo weak cache #142925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1575,6 +1575,33 @@ class EvilZoneInfo(self.klass): | |||||
| class CZoneInfoCacheTest(ZoneInfoCacheTest): | ||||||
| module = c_zoneinfo | ||||||
|
|
||||||
| def test_weak_cache_get_type_confusion(self): | ||||||
| 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): | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| class EvilCache: | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rename EvilCache with Cache and EvilZoneInfo by ZI only. In addition, create _weak_cache when instantiating the class: class ZI(self.class):
_weak_cache = Cache()Then, check the TypeError message. |
||||||
| 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 | ||||||
|
|
||||||
| 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 on lines
+1
to
+3
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This entry should be rewritten, it doesn't help for the end user. It's not necessarily a "malicious" override, it could be accidental as well. I would rather have something like :mod:`zoneinfo`: fix a crash when instantiating :class:`~zoneinfo.Zoneinfo`
objects for which the internal class-level cache is consistent. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to have a temporary variable. In addition, avoid exposing implementation details about weak_cache here. Instead, I would simply say that the instance is incorrect (something like "expecting a %T, got %T") |
||
| 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); | ||
|
|
@@ -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; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.