← 输入框|github.com|刷新
Skip to content

Code objects can contain unmarshallable objects #89290

Open

Description

@encukou
BPO 45127
Nosy @encukou, @shihai1991, @FFY00, @DiddiLeija
Files
  • reproducer_replace.py
  • reproducer_compileall.py
  • 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 = None
    created_at = <Date 2021-09-07.13:57:18.092>
    labels = []
    title = 'Code objects can contain unmarshallable objects'
    updated_at = <Date 2021-09-07.17:23:45.434>
    user = 'https://github.com/encukou'

    bugs.python.org fields:

    activity = <Date 2021-09-07.17:23:45.434>
    actor = 'shihai1991'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = []
    creation = <Date 2021-09-07.13:57:18.092>
    creator = 'petr.viktorin'
    dependencies = []
    files = ['50268', '50269']
    hgrepos = []
    issue_num = 45127
    keywords = []
    message_count = 3.0
    messages = ['401277', '401278', '401281']
    nosy_count = 4.0
    nosy_names = ['petr.viktorin', 'shihai1991', 'FFY00', 'DiddiLeija']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = None
    status = 'open'
    superseder = None
    type = None
    url = 'https://bugs.python.org/issue45127'
    versions = []

    Linked PRs

    Activity

    1. encukou commented on Sep 7, 2021

      @encukou
      MemberAuthor

      The replace method of code allows setting e.g.

      • co_filename to a subclass of str
      • co_consts to an arbitrary tuple
        and possibly more weird cases.

      This makes code objects unmarshallable.

      One way to create such a code object is to call compileall.compile_file with a str subclass as path. See the attached reproducers.

      This hit pip, see: pypa/pip#10358 (comment)

    2. encukou commented on Sep 7, 2021

      @encukou
      MemberAuthor

      How to fix this? I guess:

      • co_filename should be converted to an actual str, or reject subclasses with an exception
      • for co_consts, the marshal docs could be updated to add code objects to "containers". The code docs already say co_consts "is a tuple containing the literals"; if someone's putting in non-literals they're voiding the warranty.

      And so on for all other fields of code objects.

    3. encukou commented on Sep 7, 2021

      @encukou
      MemberAuthor

      See also gh-87005

    4. transferred this issue fromon Apr 10, 2022
    5. vasiliyk commented on Sep 24, 2026

      @vasiliyk
      Contributor

      I want to propose to keep code objects unrestricted at creation and just improve the error at save time.

      Marshal already raises on dump, but the message does not say where the problem is. A code object can contain many values, and with nested code objects it is hard to tell what MyStr refers to.

      >>> import marshal
      >>> class MyStr(str): pass
      ... marshal.dumps(compile("", MyStr("foo.py"), "exec"))
      ...
      Traceback (most recent call last):
        File "<python-input-1>", line 2, in <module>
          marshal.dumps(compile("", MyStr("foo.py"), "exec"))
          ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      ValueError: cannot marshal MyStr objects

      The error could carry context to make it more helpful:

      Traceback (most recent call last):
        File "<python-input-1>", line 2, in <module>
          marshal.dumps(compile("", MyStr("foo.py"), "exec"))
          ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      ValueError: cannot marshal MyStr objects: 'foo.py' (subclasses of str are not supported)
      when marshalling code object field co_filename
      when marshalling code object '<module>' (foo.py, line 1)

      This covers every path that creates a code object.

      Why not reject such objects at creation?

      • Most code objects are never written to a .pyc (exec, bytecode tools, replace() in tests); restricting creation would break code that works today.
      • Nothing changes for code that is not saved, so there is no compatibility concern.
      • It seems Java/.Net use a similar model (allow runtime-only code to reference arbitrary objects and restrict what can be saved). Not sure how much weight 'others do it too' can carry :]
    6. picnixz commented on Sep 24, 2026

      @picnixz
      Member
    7. picnixz commented on Sep 24, 2026

      @picnixz
      Member

      FTR, if someone messes up with code.replace, it is at their own discretion and we do not want to validate their modifications. This is why we closes previous similar issues

    8. serhiy-storchaka commented on Sep 24, 2026

      @serhiy-storchaka
      Member

      I agree with @vasiliyk. We should not restrict the use which does not involve marshalling.

      Adding notes to locate error like in pickle and json is also a good idea. It was impossibly to implement it before the changes which were made few days ago -- we did not have a raised exception, only an error code.

    9. vasiliyk commented on Oct 21, 2026

      @vasiliyk
      Contributor

      I have implemented the notes support for marshalling, but I am at the open PR limit that was set on Oct 20 for the repo.

      The notes follow pickle's convention: one note per enclosing level, innermost first, with the same wording for container items, dict values and set elements.

      3 differences with pickle:

      • a failing dict key gets a note, pickle leaves it unannotated;
      • a failing slice field is named directly (start, stop, step), pickle reports a tuple index inside the reconstructor arguments;
      • code object fields are identified by name, qualified name, file and line, pickle rejects code objects.

      current:

      >>> import marshal
      >>> class MyStr(str): pass
      ... marshal.dumps(compile("def f(): pass", MyStr("foo.py"), "exec"))
      ... 
      Traceback (most recent call last):
        File "<python-input-1>", line 2, in <module>
          marshal.dumps(compile("def f(): pass", MyStr("foo.py"), "exec"))
          ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      ValueError: cannot marshal MyStr objects

      with notes:

      >>> import marshal
      >>> class MyStr(str): pass
      ... 
      >>> marshal.dumps(compile("def f(): pass", MyStr("foo.py"), "exec"))
      Traceback (most recent call last):
        File "<python-input-2>", line 1, in <module>
          marshal.dumps(compile("def f(): pass", MyStr("foo.py"), "exec"))
          ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      ValueError: cannot marshal MyStr objects
      when marshalling co_filename of code object f (foo.py, line 1)
      when marshalling tuple item 0
      when marshalling co_consts of code object <module> (foo.py, line 1)

      New tests cover every container type, slice field and code object field.
      I will open the PR as soon as one of my open PRs is merged.

    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

      interpreter-core(Objects, Python, Grammar, and Parser dirs)type-featureA feature request or enhancement

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions