)]}'
{
  "commit": "9e9dbae858f75c8b886f1aea642418150cb8aed0",
  "tree": "781248449225c70f5c82a5c28d29cd4468aec678",
  "parents": [
    "69f97e01f74d7c0bc7b429f3aa471a2c22855379"
  ],
  "author": {
    "name": "vhulto",
    "email": "164919528+vhullto@users.noreply.github.com",
    "time": "Sun Jun 28 13:40:37 2026 -0700"
  },
  "committer": {
    "name": "Copybara-Service",
    "email": "copybara-worker@google.com",
    "time": "Sun Jun 28 13:43:09 2026 -0700"
  },
  "message": "Python: fix heap-use-after-free in MapIterator after map.clear() (#27257)\n\n## Bug\n\n`Clear()` in `map_container.cc` (line 294-302) calls\n`reflection-\u003eClearField()` which destroys all underlying map nodes via\n`ClearTable(reset\u003dtrue)`, but does not increment `self-\u003eversion`.\n\nAll other mutators (ScalarMapSetItem, MessageMapSetItem, MergeFrom, etc.)\nincrement `self-\u003eversion` after mutation. `IterNext()` relies on version\nmismatch to detect concurrent modification and raise `RuntimeError`.\nWithout the version bump, a live iterator proceeds to dereference the\nfreed `NodeBase*` via `SetMapIteratorValue` → `UntypedMapIterator::PlusPlus`.\n\n**ASAN confirmed:** heap-use-after-free, READ size 8 at\n`UntypedMapIterator::PlusPlus` (map.h:599), freed by `ClearTable`\n(map.h:345), allocated by `ScalarMapSetItem` (map_container.cc:416).\n\n## Fix\n\nAdd `self-\u003eversion++` after `ClearField` in `Clear()`, matching every\nother mutator in the same file.\n\n## Reproducer\n\n```python\nmsg \u003d M()  # proto3 with map\u003cstring, int32\u003e mp\nfor k in (\"a\",\"b\",\"c\",\"d\"): msg.mp[k] \u003d 1\nit \u003d iter(msg.mp)\nnext(it)\nmsg.mp.clear()   # frees nodes, version NOT bumped\nnext(it)         # heap-use-after-free\n```\n\nCloses #27257\n\nCOPYBARA_INTEGRATE_REVIEW\u003dhttps://github.com/protocolbuffers/protobuf/pull/27257 from vhullto:fix/python-map-clear-uaf fb352251107932570e8ec5ba293b18e8d74521b6\nPiperOrigin-RevId: 939482747\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "4ae6ae8618625844a0e3b75f7e7db94ca0e16917",
      "old_mode": 33261,
      "old_path": "python/google/protobuf/internal/message_test.py",
      "new_id": "7c10d788bd9ded8d16f8482842d7b73862c5f0ec",
      "new_mode": 33261,
      "new_path": "python/google/protobuf/internal/message_test.py"
    },
    {
      "type": "modify",
      "old_id": "f88171ed628259f8e26afcee928d43f8c255ecc8",
      "old_mode": 33188,
      "old_path": "python/google/protobuf/pyext/map_container.cc",
      "new_id": "4b6b71ef92f29064780fb91ebcd3a4f6740e455b",
      "new_mode": 33188,
      "new_path": "python/google/protobuf/pyext/map_container.cc"
    }
  ]
}
