You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 688e743
Browse filesBrowse the repository at this point in the historyBrowse files
authored
fix: never run keep_alive for a failed overload or failed return conversion (#6183)
* fix: do not run keep_alive for an overload that failed argument conversion
Calling an overloaded function bound with `py::keep_alive<0, N>` segfaults
whenever the call is not matched by the first overload tried.
.def("make", &Holder::from_int, py::keep_alive<0, 1>())
.def("make", &Holder::from_string, py::keep_alive<0, 1>());
h.make(1) # fine
h.make("x") # SIGSEGV
The dispatch lambda in `cpp_function::initialize` invokes the post-call hook
unconditionally:
auto result = call_impl<...>(call, ...);
process_attributes<Extra...>::postcall(call, result);
but `call_impl` returns `PYBIND11_TRY_NEXT_OVERLOAD` when `load_args` fails,
and that sentinel is `((PyObject *) 1)` rather than an object. For a
`keep_alive` whose nurse or patient is index 0 the work happens in postcall,
so `keep_alive_impl` receives the sentinel as `ret`, hands it to `get_arg(0)`
and dereferences it in `_Py_TYPE`.
The existing guards do not catch it: the sentinel is neither null nor
`Py_None`, so it passes straight through the checks added in #341.
Guard inside `keep_alive_impl`, next to those checks. Only the
`Nurse == 0 || Patient == 0` specialization does its work in postcall, and
`keep_alive` is the only call policy with a non-trivial postcall, so this
covers every path that can observe the sentinel. `keep_alive<1, 2>` and
friends run in precall against fully populated `call.args` and are unaffected.
The regression test crashes the interpreter without the fix. Reaching the
second overload is what matters: it is the first overload's failed conversion
that produces the sentinel.
* fix: never run keep_alive for a failed overload or failed return conversion
Move keep_alive activation entirely to postcall, so an argument-to-argument
keep_alive no longer fires in precall for an overload that later fails
argument conversion. Extend the postcall guard to also skip a null return
handle, so a failed return-value conversion raises its own error instead of
"Could not activate keep_alive!". Document that postcall can receive the
PYBIND11_TRY_NEXT_OVERLOAD sentinel or a null handle.
Tests cover the sentinel as nurse and as patient, the argument-to-argument
case, and the failed return conversion, and no longer make the test object
its own patient.
Assisted-by: ClaudeCode:claude-fable-5
Claude-Session: https://claude.ai/code/session_01WRPhwYhrJKvFXU2NWe1p3W
* fix: run argument-to-argument keep_alive before the call again
Moving keep_alive<N, P> (N, P != 0) to postcall meant a keep_alive error
was raised after the function ran, so its side effects stayed. Run the
precall hook in call_impl after load_args succeeds instead, so a failed
overload still does not trigger it and an error still stops the call.
Also release the return value when a postcall keep_alive throws, as the
dispatcher drops it.
Assisted-by: ClaudeCode:claude-opus-5-5
* test: collect the child before capturing output on PyPy
Assisted-by: ClaudeCode:claude-opus-5-5
* fix: skip postcall for an overload that failed argument conversion
The dispatcher now runs postcall only for the overload that matched, like
precall. Add a test for a rejected sole candidate, and clarify the
postcall and keep_alive_impl comments.
Assisted-by: ClaudeCode:claude-opus-5-5
* fix: complete call policy conversion and result cleanup
Run precall after cast_op and parameter conversion without adding copies or moves, and keep it before guard construction. Protect the owned return throughout postcall so any throwing hook releases it.
Extend regression coverage for reference rejection, converting overloads, constructors, return retention, custom hooks and return conversion errors.
* refactor: clarify call guard conversion boundaries
Remove unused argument-loader invocation overloads, standardize precall naming, and explain the function_ref parameter boundary and copy elision.
Document guard ordering and test caster rejection, guarded calls, by-value parameter destruction, and extraction before GIL release.
* test: make call guard caster extraction const
* test: explain moved-from lifetime logging [skip ci]
---------
Co-authored-by: Justin Turney <justin.turney@gmail.com>
Co-authored-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com>
0 commit comments