Skip to content

Operand coercion breaks MappingProxyType encapsulation #88762

Closed

Description

@ncoghlan
BPO 44596
Nosy @ncoghlan, @serhiy-storchaka
Superseder
  • bpo-43838: There is a way to access an underlying mapping in MappingProxyType
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2021-07-10.10:00:37.729>
    created_at = <Date 2021-07-10.06:55:40.079>
    labels = ['type-bug', '3.11']
    title = 'Operand coercion breaks MappingProxyType encapsulation'
    updated_at = <Date 2021-07-10.10:00:37.726>
    user = 'https://github.com/ncoghlan'

    bugs.python.org fields:

    activity = <Date 2021-07-10.10:00:37.726>
    actor = 'serhiy.storchaka'
    assignee = 'none'
    closed = True
    closed_date = <Date 2021-07-10.10:00:37.729>
    closer = 'serhiy.storchaka'
    components = []
    creation = <Date 2021-07-10.06:55:40.079>
    creator = 'ncoghlan'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 44596
    keywords = []
    message_count = 2.0
    messages = ['397242', '397243']
    nosy_count = 2.0
    nosy_names = ['ncoghlan', 'serhiy.storchaka']
    pr_nums = []
    priority = 'normal'
    resolution = 'duplicate'
    stage = 'resolved'
    status = 'closed'
    superseder = '43838'
    type = 'behavior'
    url = 'https://bugs.python.org/issue44596'
    versions = ['Python 3.11']

    Activity

    1. ncoghlan commented on Jul 10, 2021

      @ncoghlan
      ContributorAuthor

      The fast locals proxy implementation for PEP-558 used the existing MappingProxyType implementation as a starting point.

      This meant I noticed the following code in the mapping proxy comparison implementation:

      ======

      static PyObject *
      mappingproxy_richcompare(mappingproxyobject *v, PyObject *w, int op)
      {
          return PyObject_RichCompare(v->mapping, w, op);
      }

      ======

      Seeing that code lead to trying the following experiment:

      ==========

      >>> from types import MappingProxyType
      >>> d = {}
      >>> proxy = MappingProxyType(d)
      >>> class Sneaky:
      ...     def __eq__(self, other):
      ...         if other is d:
      ...             raise RuntimeError("Broke encapsulation")
      ... 
      >>> proxy == Sneaky()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "<stdin>", line 4, in __eq__
      RuntimeError: Broke encapsulation

      ==========

      The new __or__ implementation has a corresponding loophole:

      ========

      >>> class SneakyOr:
      ...     def __or__(self, other):
      ...         if other is d:
      ...             raise RuntimeError("Broke encapsulation")
      ...     def __ror__(self, other):
      ...         return self.__or__(other)
      ... 
      >>> proxy | SneakyOr()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
        File "<stdin>", line 6, in __ror__
        File "<stdin>", line 4, in __or__
      RuntimeError: Broke encapsulation

      ========

      Delegating these operations to the underlying mapping via the abstract operation layer means that the underlying mapping gets exposed to the operand coercion machinery and can hence be accessed by the other operand.

    2. added
      3.11only security fixes
      type-bugAn unexpected behavior, bug, or error
      on Jul 10, 2021
    3. serhiy-storchaka commented on Jul 10, 2021

      @serhiy-storchaka
      Member

      It is a duplicate of bpo-43838.

    4. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.11only security fixestype-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions