)]}'
{
  "commit": "5540f968020451da2c1bf7211b8c861c0fc30cbf",
  "tree": "e729027b40a925e5c45348dde6a672cd8d72c7b9",
  "parents": [
    "a647cddd4e9f44c033eeb1d423e1bf86c6c198c1"
  ],
  "author": {
    "name": "Henry Schreiner",
    "email": "HenrySchreinerIII@gmail.com",
    "time": "Tue Jul 28 21:53:36 2026 -0400"
  },
  "committer": {
    "name": "GitHub",
    "email": "noreply@github.com",
    "time": "Tue Jul 28 18:53:36 2026 -0700"
  },
  "message": "fix: only add string_view life support for transient sources (#6096)\n\n* revert: \"revert: add life support to handles cast to string_view (#6092)\"\n\nThis re-applies #6092 (reverting #6097) so the follow-up fixes in this PR can build on it.\n\nAssisted-by: ClaudeCode:claude-opus-4.8\n\n* fix: don\u0027t throw from string_view life support outside a bound function\n\nPR #6092 added loader_life_support::add_patient(src) to keep the source\nobject alive when loading a string view, fixing a real use-after-free when\na container of views is built from a non-sequence iterable (e.g. a\ngenerator): list_caster materializes a temporary tuple that owns the\nstrings and destroys it when load() returns, before the bound function\nbody runs.\n\nadd_patient throws when there is no life support frame, so casting to a\nview outside a bound function (e.g. a manual py::cast\u003cstd::string_view\u003e)\nnow raises instead of relying on the caller-owned source, a regression\nfrom #6092.\n\nFor these view-into-src cases registration is best effort: inside a bound\nfunction it keeps src alive (fixing the UAF), and outside one the caller\nowns src\u0027s lifetime as before. Add try_add_patient(), which returns false\ninstead of throwing when there is no frame, and use it at the three view\nload sites. add_patient() keeps its strict contract for value-creating\nconversions.\n\nAssisted-by: ClaudeCode:claude-opus-4.8\n\n* fix: only add string_view life support for transient sources\n\nRefine the previous commit. Best-effort registration (try_add_patient)\nsilently produces a dangling view when a container of views is built from\na generator outside a bound function: there the materialized temporary is\nreleased before the view is used, and with no frame nothing keeps it\nalive. Such a cast cannot be made safe, so it should fail loudly, while a\nview into a durable, caller-owned object needs no life support at all.\n\nThe view caster cannot tell a durable source from a pybind11-managed\ntransient one; that provenance lives in the container caster. Introduce an\nambient transient_source_guard that the list, set, map, and array casters\nset around their generator/materialized paths, and have the string caster\nkeep the source alive only when loading from a transient source (via the\nthrowing add_patient, so try_add_patient is no longer needed). This means:\n\n- views into durable sources (direct arguments, sequences, manual casts)\n  add no life support and no longer throw outside a bound function, and\n- a generator used outside a frame throws, rather than silently dangling.\n\nThe guard restores (rather than clears) the previous value, so a durable\ncontainer nested in a transient one is correctly treated as transient.\n\nVerified with AddressSanitizer: the in-frame generator case is clean, the\nout-of-frame durable cases succeed, and the out-of-frame generator case\nthrows.\n\nAssisted-by: ClaudeCode:claude-opus-4.8\n\n* Revert \"fix: only add string_view life support for transient sources\"\n\nThis reverts commit e18b8346a28c125964fcb9d192e6643d5ef3b420.\n\n* test: cover string_view argument life support\n\n* test: cover generated and nested string_view lifetimes\n\n* test: cover temporary-backed string_view casts\n\n* test: explain string_view lifetime regression tests\n\n* docs: clarify string_view lifetime requirements\n\n* docs: explain life support for custom view casters\n\n---------\n\nCo-authored-by: Ralf W. Grosse-Kunstleve \u003crgrossekunst@nvidia.com\u003e",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "786192beebca5e0426668d50fea5f486d14077d0",
      "old_mode": 33188,
      "old_path": "docs/advanced/cast/custom.rst",
      "new_id": "de673b2d86a57e45056353447deb2f1d5d4bedb3",
      "new_mode": 33188,
      "new_path": "docs/advanced/cast/custom.rst"
    },
    {
      "type": "modify",
      "old_id": "1e17bc389cacc3c86af1419594d1bbdeac4ce866",
      "old_mode": 33188,
      "old_path": "docs/advanced/cast/stl.rst",
      "new_id": "6abe8b467d7fb55067cdfd0c56afca895aff49a9",
      "new_mode": 33188,
      "new_path": "docs/advanced/cast/stl.rst"
    },
    {
      "type": "modify",
      "old_id": "271716b4b300a076be22cf005d004f615f0d0470",
      "old_mode": 33188,
      "old_path": "docs/advanced/cast/strings.rst",
      "new_id": "2b24d7cb9971ff1456150fd9cb7890a549266611",
      "new_mode": 33188,
      "new_path": "docs/advanced/cast/strings.rst"
    },
    {
      "type": "modify",
      "old_id": "93e1a94d8fbb987d15558d71c919fa1b3d033345",
      "old_mode": 33188,
      "old_path": "docs/advanced/pycpp/object.rst",
      "new_id": "d8661c2ce73419aa7f25343ab997ab1217a8ed14",
      "new_mode": 33188,
      "new_path": "docs/advanced/pycpp/object.rst"
    },
    {
      "type": "modify",
      "old_id": "62ca45a09ad6034d3c34858a535069937b4d8850",
      "old_mode": 33188,
      "old_path": "include/pybind11/cast.h",
      "new_id": "9ab6e332d1744bf6a5c004e721df1729084b20ef",
      "new_mode": 33188,
      "new_path": "include/pybind11/cast.h"
    },
    {
      "type": "modify",
      "old_id": "8fbf700e124c9d29d29dc7b08e9713e3d5161393",
      "old_mode": 33188,
      "old_path": "include/pybind11/detail/type_caster_base.h",
      "new_id": "b6d03ca903ced34361230e4348f9d5e3ceb8f466",
      "new_mode": 33188,
      "new_path": "include/pybind11/detail/type_caster_base.h"
    },
    {
      "type": "modify",
      "old_id": "8bddbb1f38d9be5677138a21c8dd8aca695360ae",
      "old_mode": 33188,
      "old_path": "tests/test_stl.cpp",
      "new_id": "b5d1979d04e31dc145b47da015c065adeb113902",
      "new_mode": 33188,
      "new_path": "tests/test_stl.cpp"
    },
    {
      "type": "modify",
      "old_id": "b04f55c9f8cfcbfa00d2185a21a356459eb5e183",
      "old_mode": 33188,
      "old_path": "tests/test_stl.py",
      "new_id": "c75ccb8d240bcc6469500c2c2a3e4fedd41dcae3",
      "new_mode": 33188,
      "new_path": "tests/test_stl.py"
    },
    {
      "type": "modify",
      "old_id": "4103c0f5ff1e574c59628ac5d0680c655cc3eaf9",
      "old_mode": 33188,
      "old_path": "tests/test_with_catch/test_interpreter.cpp",
      "new_id": "daa1041bf537f2965cae72feaa0d36555a231031",
      "new_mode": 33188,
      "new_path": "tests/test_with_catch/test_interpreter.cpp"
    }
  ]
}
