Skip to content

CPython 3.15: consolidated memory-safety and concurrency bugs #157198

Closed as not planned

Description

@Nievesjyl

Hello,

This issue consolidates 13 reproducible CPython 3.15 bugs involving memory safety and concurrency.

The bugs resulted from Python security research using static analysis. Each entry has a concrete PoC in the linked version-specific Gist. Although the collection is large, these bugs warrant inspection, especially the cases producing interpreter crashes, sanitizer failures, memory corruption, or similarly concrete failures.

Bugs

  1. AST initialization _field_types race — Concurrent descriptor replacement can reclaim a borrowed value and cause an impossible initialization decision.
  2. Bytes formatting shared-bytearray race — A peer can modify bytearray storage while %b formatting copies it, producing a mixed-generation result.
  3. Verbose module-finalization stderr reentry — A sys.__stderr__ callback can release borrowed sys.modules entries before finalization uses them.
  4. Unexpected-keyword dictionary traversal race — Concurrent mutation can reclaim a borrowed keyword while diagnostics convert and format it.
  5. Import traceback-chain frame race — Traceback mutation can detach a node and release its borrowed frame during import cleanup.
  6. Child memoryview construction and source-release race — Source release can deallocate the managed buffer between the released check and child export acquisition.
  7. object.__getstate__() inline-dictionary transition race — Concurrent dictionary replacement can make a nonempty object incorrectly report an empty state.
  8. OrderedDict reentrant keys-table corruption — A colliding comparison can replace the cached keys table, causing an unrelated key to move.
  9. _posixsubprocess.fork_exec() process-argument race — Concurrent list replacement can finalize a borrowed argument before filesystem conversion.
  10. BinaryReader operation and close race — get_stats() or callback-resumed replay can access native reader state freed by close().
  11. Concurrent BinaryReader.replay() state corruption — Simultaneous replays can corrupt shared reconstruction arrays, stacks, depths, and counters.
  12. BinaryWriter.get_stats() and close race — Concurrent close() can destroy the native writer before an unlocked stats call finishes using it.
  13. BinaryWriter.write_sample() input-list race — Concurrent mutation can reclaim outer, thread, or frame-list items before their fields are read.

Activity

  1. Nievesjyl commented on Sep 8, 2026

    @Nievesjyl
    Author

    This issue is associated with #157197 and #157196

  2. Nievesjyl commented on Sep 8, 2026

    @Nievesjyl
    Author

    The cross-version review identifies the following 78 bugs as shared across CPython 3.13, 3.14, and 3.15. Each item links to its detailed report and concrete PoC in the consolidated CPython 3.14 Gist.

    1. structseq-type-metadata-borrow-race — Struct-sequence operations consume concurrently replaced type metadata through a borrowed dictionary value.
    2. frame-locals-proxy-items-race — FrameLocalsProxy.items() traverses a concurrently mutated extra-locals dictionary.
    3. type-subclasses-bases-race — type.__subclasses__() traverses a subclass registry concurrently modified by __bases__ assignment.
    4. assemble-metadata-dict-race — Code assembly traverses caller-owned metadata dictionaries without a coherent snapshot.
    5. contextvars-hamt-shared-iterator-next — Concurrent advancement corrupts a shared contextvars.Context iterator.
    6. elementtree-child-read-mutation — _elementtree child readers race structural mutation.
    7. elementtree-get-dict-value-borrow-promotion — Element.get() promotes a concurrently removed attribute-dictionary value.
    8. itertools-tee-data-shared-values — _tee_dataobject copies an unlocked shared list.
    9. pickle-shared-unpickler-running-guard — Concurrent load calls bypass the Unpickler running guard.
    10. pickle-unpickler-memo-proxy-race — Concurrent Unpickler memo-proxy operations race.
    11. pyexpat-handler-slot-race — pyexpat.xmlparser handler slots are unsynchronized.
    12. pyexpat-shared-parser-native-state-race — Shared pyexpat parser native state races.
    13. pyfunction-getannotations-race — PyFunction_GetAnnotations() has an unlocked annotation-state race.
    14. pysys-getobject-borrowed-return — PySys_GetObject() can return a borrowed object concurrently removed from sys.__dict__.
    15. ssl-weak-socket-borrow — The SSL weak-socket helper returns a referent after dropping its temporary owner.
    16. unicode-fromid-array-growth-race — _PyUnicode_FromId() identifier-array growth races readers.
    17. bytearray-init-unlocked-storage — bytearray.__init__() races storage replacement.
    18. ctypes-shared-anonymous-list — ctypes consumes a concurrently mutated _anonymous_ list.
    19. ctypes-shared-fields-list — ctypes consumes a concurrently mutated _fields_ list.
    20. dict-merge-seq2-borrow-promotion — Dictionary merge consumes borrowed sequence entries during mutation.
    21. elementtree-append-clear — Element append races clear on the child array.
    22. elementtree-child-write-mutation — Element child writers race structural mutation.
    23. elementtree-setevents-shared-input-list — XMLParser._setevents() consumes a concurrently mutated list.
    24. elementtree-shared-input-list — Element mutation consumes a concurrently mutated input list.
    25. instruction-sequence-cfg-race — CFG conversion traverses concurrently reallocated InstructionSequence storage.
    26. instruction-sequence-concurrent-mutation — InstructionSequence operations race internal storage mutation.
    27. pickle-bytearray-unpinned-storage — Pickle reads bytearray storage while it is resized.
    28. pickle-fix-imports-unpinned-mapping — Pickle fix_imports borrows mutable mapping entries.
    29. pickle-load-extension-unpinned-registry — Pickle extension loading borrows mutable registry entries.
    30. pickle-memo-setter-shared-dict-iteration — A pickle memo setter iterates a concurrently mutated dictionary.
    31. pickle-dispatch-table-unpinned-reducer — Pickle promotes a reducer borrowed from the mutable copyreg dispatch table.
    32. pickle-extension-cache-borrow-promotion — Pickle promotes an object borrowed from the mutable extension cache.
    33. pickle-load-build-shared-state-iteration — Pickle BUILD iterates a concurrently mutated shared state dictionary.
    34. pickle-find-class-unpinned-mapping — Pickle find_class borrows mutable compatibility-mapping entries.
    35. posix-spawn-file-actions-list-race — posix_spawn consumes a concurrently mutated file-actions list.
    36. prep-reraise-star-shared-list — Exception-group reraising consumes a concurrently mutated list.
    37. pyimport-getimporter-path-hooks-race — PyImport_GetImporter() borrows mutable path-hook entries.
    38. rawiobase-readinto-buffer-resize — A RawIOBase.read() buffer is resized during a readinto() callback.
    39. select-shared-fd-list — select() consumes a concurrently mutated descriptor list.
    40. source-bytearray-unpinned-storage — Source conversion reads bytearray storage while it is resized.
    41. sqlite-shared-connection-cursor-registry — SQLite cursor-registry cleanup races concurrent cursor creation.
    42. sqlite-shared-parameter-list — SQLite binds a concurrently mutated parameter list.
    43. sre-compile-shared-code-list — SRE compilation consumes a concurrently mutated code list.
    44. sre-template-shared-list — SRE template construction consumes a concurrently mutated list.
    45. structseq-list-borrow-promotion — Struct-sequence construction borrows concurrently replaced list entries.
    46. suggestions-shared-candidates-list — Suggestion generation consumes a concurrently mutated candidate list.
    47. syslog-argv0-borrow-promotion — Syslog borrows sys.argv[0] during concurrent replacement.
    48. termios-tcsetattr-shared-attributes — tcsetattr() consumes a concurrently mutated attributes list.
    49. tkinter-flatten-shared-list — Tkinter flattening consumes a concurrently mutated list.
    50. traceback-syspath-item-borrow — Traceback source lookup borrows mutable sys.path entries.
    51. json-dict-borrow-promotion — JSON exact-dictionary traversal promotes concurrently removed entries.
    52. json-mapping-items-borrow-promotion — JSON custom-mapping traversal promotes entries from a concurrently mutated items list.
    53. json-sequence-borrow-promotion — JSON sequence traversal promotes concurrently removed list entries.
    54. memoryview-tobytes-release — memoryview.tobytes() copies released and resized exporter storage.
    55. unicode-encoder-shared-bytearray — Unicode encoding copies storage from a concurrently resized bytearray.
    56. crossinterp-shared-namespace-dict — Cross-interpreter namespace conversion traverses and borrows from a concurrently mutated dictionary.
    57. decimal-signal-dict-borrow-promotion — Decimal signal conversion consumes a value borrowed from a concurrently mutated dictionary.
    58. module-dir-borrow-promotion — module.__dir__() calls a value borrowed from a concurrently mutated module dictionary.
    59. gc-callback-borrow-promotion — GC callback iteration promotes a callable borrowed from the mutable gc.callbacks list.
    60. pyobject-call-shared-kwargs — PyObject_Call() traverses a concurrently mutated caller-supplied kwargs dictionary.
    61. marshal-dumps-shared-containers — marshal.dumps() traverses a concurrently mutated caller-supplied dictionary.
    62. float-bytearray-unpinned-storage — Float conversion parses bytearray storage concurrently resized between complete generations.
    63. pyarg-et-bytearray-resize — PyArg encoded-string conversion copies a bytearray concurrently resized between complete generations.
    64. pyexpat-parsefile-bytearray-resize — ParseFile() consumes a returned bytearray while another thread replaces its contents.
    65. pynumber-long-bytearray-storage — Integer conversion parses bytearray storage concurrently resized between complete digit generations.
    66. struct-pack-bytearray-unpinned-storage — struct.pack() copies bytearray storage concurrently resized between complete generations.
    67. array-fromlist-borrow-promotion — array.fromlist() consumes entries from a concurrently slice-replaced exact list.
    68. decimal-format-override-borrow-promotion — Decimal formatting consumes override strings borrowed from a concurrently mutated dictionary.
    69. ssl-password-bytearray-resize — SSL certificate password setup copies concurrently resized bytearray storage without a buffer lease.
    70. marshal-load-retained-readinto-view — marshal.load() exposes a memoryview of its reallocatable internal read buffer to caller readinto() code.
    71. multibyte-streamreader-shared-read — Concurrent reads corrupt a shared multibyte stream reader's pending input and decoder state.
    72. codec-cache-item-borrow-promotion — Codec lookup returns an item borrowed from a cache tuple after dropping its sole local tuple owner.
    73. elementtree-entity-unpinned-value — ElementTree entity expansion uses a dictionary value borrowed across a callback while a peer replaces it.
    74. memoryview-hex-noncontiguous-release — memoryview.hex() copies non-contiguous backing storage after another thread releases the view.
    75. memoryview-hash-release — Memoryview hashing drops its temporary export before hashing the captured backing address.
    76. memoryview-cast-shape-borrow — memoryview.cast() consumes an element borrowed from a concurrently mutated shape list.
    77. flowgraph-consts-list-race — Flowgraph optimization borrows constants from a concurrently mutated caller list.
    78. pickle-donor-memo-copy-race — Assigning an Unpickler memo proxy copies slots from a concurrently cleared and reloaded donor memo.
  3. picnixz commented on Sep 9, 2026

    @picnixz
    Member

    ElementTree is not currently not safe in FT so all issues are invalid as of now. Thdre is alsona separate issue about making etree FT-safe. So please in the future avoid dumping issues in a bulk if there is alredy an open one (or comment on that one directly)

  4. picnixz commented on Sep 9, 2026

    @picnixz
    Member

    There are also exisring issues for array.* i believe so clean up your list first please.

  5. Nievesjyl commented on Sep 9, 2026

    @Nievesjyl
    Author

    There are also exisring issues for array.* i believe so clean up your list first please.

    Thanks for the response. We’ll do the cleanup ASAP.

  6. Nievesjyl commented on Sep 11, 2026

    @Nievesjyl
    Author

    We are preparing a carefully screened report that complies with CPython policy, so we are closing this issue.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions