Kaydet (Commit) dcebe0f2 authored tarafından Michael Foord's avatar Michael Foord

Closes issue 11133. Fixes two cases where inspect.getattr_static could trigger code execution

üst c867239a
...@@ -589,13 +589,10 @@ but avoids executing code when it fetches attributes. ...@@ -589,13 +589,10 @@ but avoids executing code when it fetches attributes.
that raise AttributeError). It can also return descriptors objects that raise AttributeError). It can also return descriptors objects
instead of instance members. instead of instance members.
.. versionadded:: 3.2 If the instance `__dict__` is shadowed by another member (for example a
property) then this function will be unable to find instance members.
The only known case that can cause `getattr_static` to trigger code execution, .. versionadded:: 3.2
and cause it to return incorrect results (or even break), is where a class uses
:data:`~object.__slots__` and provides a `__dict__` member using a property or
descriptor. If you find other cases please report them so they can be fixed
or documented.
`getattr_static` does not resolve descriptors, for example slot descriptors or `getattr_static` does not resolve descriptors, for example slot descriptors or
getset descriptors on objects implemented in C. The descriptor object getset descriptors on objects implemented in C. The descriptor object
......
...@@ -1069,15 +1069,16 @@ def _check_instance(obj, attr): ...@@ -1069,15 +1069,16 @@ def _check_instance(obj, attr):
instance_dict = object.__getattribute__(obj, "__dict__") instance_dict = object.__getattribute__(obj, "__dict__")
except AttributeError: except AttributeError:
pass pass
return instance_dict.get(attr, _sentinel) return dict.get(instance_dict, attr, _sentinel)
def _check_class(klass, attr): def _check_class(klass, attr):
for entry in _static_getmro(klass): for entry in _static_getmro(klass):
try: if not _shadowed_dict(type(entry)):
return entry.__dict__[attr] try:
except KeyError: return entry.__dict__[attr]
pass except KeyError:
pass
return _sentinel return _sentinel
def _is_type(obj): def _is_type(obj):
...@@ -1087,6 +1088,19 @@ def _is_type(obj): ...@@ -1087,6 +1088,19 @@ def _is_type(obj):
return False return False
return True return True
def _shadowed_dict(klass):
dict_attr = type.__dict__["__dict__"]
for entry in _static_getmro(klass):
try:
class_dict = dict_attr.__get__(entry)["__dict__"]
except KeyError:
pass
else:
if not (type(class_dict) is types.GetSetDescriptorType and
class_dict.__name__ == "__dict__" and
class_dict.__objclass__ is entry):
return True
return False
def getattr_static(obj, attr, default=_sentinel): def getattr_static(obj, attr, default=_sentinel):
"""Retrieve attributes without triggering dynamic lookup via the """Retrieve attributes without triggering dynamic lookup via the
...@@ -1101,8 +1115,9 @@ def getattr_static(obj, attr, default=_sentinel): ...@@ -1101,8 +1115,9 @@ def getattr_static(obj, attr, default=_sentinel):
""" """
instance_result = _sentinel instance_result = _sentinel
if not _is_type(obj): if not _is_type(obj):
instance_result = _check_instance(obj, attr)
klass = type(obj) klass = type(obj)
if not _shadowed_dict(klass):
instance_result = _check_instance(obj, attr)
else: else:
klass = obj klass = obj
......
...@@ -906,6 +906,53 @@ class TestGetattrStatic(unittest.TestCase): ...@@ -906,6 +906,53 @@ class TestGetattrStatic(unittest.TestCase):
self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3) self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3)
self.assertEqual(inspect.getattr_static(Something, 'foo'), 3) self.assertEqual(inspect.getattr_static(Something, 'foo'), 3)
def test_dict_as_property(self):
test = self
test.called = False
class Foo(dict):
a = 3
@property
def __dict__(self):
test.called = True
return {}
foo = Foo()
foo.a = 4
self.assertEqual(inspect.getattr_static(foo, 'a'), 3)
self.assertFalse(test.called)
def test_custom_object_dict(self):
test = self
test.called = False
class Custom(dict):
def get(self, key, default=None):
test.called = True
super().get(key, default)
class Foo(object):
a = 3
foo = Foo()
foo.__dict__ = Custom()
self.assertEqual(inspect.getattr_static(foo, 'a'), 3)
self.assertFalse(test.called)
def test_metaclass_dict_as_property(self):
class Meta(type):
@property
def __dict__(self):
self.executed = True
class Thing(metaclass=Meta):
executed = False
def __init__(self):
self.spam = 42
instance = Thing()
self.assertEqual(inspect.getattr_static(instance, "spam"), 42)
self.assertFalse(Thing.executed)
class TestGetGeneratorState(unittest.TestCase): class TestGetGeneratorState(unittest.TestCase):
......
...@@ -40,6 +40,9 @@ Core and Builtins ...@@ -40,6 +40,9 @@ Core and Builtins
Library Library
------- -------
- Issue #11133: fix two cases where inspect.getattr_static can trigger code
execution. Patch by Daniel Urban.
- Issue #11501: disutils.archive_utils.make_zipfile no longer fails if zlib is - Issue #11501: disutils.archive_utils.make_zipfile no longer fails if zlib is
not installed. Instead, the zipfile.ZIP_STORED compression is used to create not installed. Instead, the zipfile.ZIP_STORED compression is used to create
the ZipFile. Patch by Natalia B. Bidart. the ZipFile. Patch by Natalia B. Bidart.
...@@ -48,7 +51,7 @@ Library ...@@ -48,7 +51,7 @@ Library
encoding was not done if euc-jp or shift-jis was specified as the charset. encoding was not done if euc-jp or shift-jis was specified as the charset.
- Issue #11500: Fixed a bug in the os x proxy bypass code for fully qualified - Issue #11500: Fixed a bug in the os x proxy bypass code for fully qualified
IP addresses in the proxy exception list. IP addresses in the proxy exception list.
- Issue #11491: dbm.error is no longer raised when dbm.open is called with - Issue #11491: dbm.error is no longer raised when dbm.open is called with
the "n" as the flag argument and the file exists. The behavior matches the "n" as the flag argument and the file exists. The behavior matches
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment