Skip to content

pybind11 wrapped method segfaults if a function returns unique_ptr<T> when holder is shared_ptr<T> #1138

Description

@EricCousineau-TRI

Testing out alternatives w.r.t. #1132, I was testing out the behavior of something like this:

classA {
public:A(int value)
: value_(value) {}
intvalue() const { return value_; }
private:int value_{};
};
unique_ptr<A> create_instance() {
return make_unique<A>(50);
}
shared_ptr<A> check_creation(py::function py_factory) {
shared_ptr<A> obj;
{
py::object py_obj = py_factory();
obj = py::cast<shared_ptr<A>>(py_obj);
// ERROR: Segfault here. I believe it may be due to a double-free, since in one of the implicit `py::cast<>` calls, the move'd `unique_ptr<>` is destructed while still containing a reference.
}
return obj;
}
...
py::class_<A, std::shared_ptr<A>>(m, "A")
.def(py::init<int>())
.def("value", &A::value);
m.def("create_instance", &create_instance);
m.def("check_creation", &check_creation);
...
py::exec(R"(factory = lambda: m.create_instance()obj = m.check_creation(factory)print(obj.value()))");

Issue

It seems that move_only_holder_caster is not aggressive enough when calling get(), and it will double-free with a non-empty unique_ptr<T>:

static handle cast(holder_type &&src, return_value_policy, handle) {
auto *ptr = holder_helper<holder_type>::get(src);
return type_caster_base<type>::cast_holder(ptr, &src);
}

template <typename T>
structholder_helper {
staticautoget(const T &p) -> decltype(p.get()) { return p.get(); }
};

Potential Solution

I believe the simple solution would be to teach holder_helper<holder_type>::get(src) to use release() for holder_type&& when holder_type = unique_ptr<U, ...>.

Workaround

The simple workaround is to explicitly cast to shared_ptr<T> in C++, since it can be implicitly constructed from unique_ptr<T>. This sidesteps pybind11 destructing a non-empty but unused unique_ptr<T>:

shared_ptr<A> create_instance() {
return make_unique<A>(50);
}

Activity

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

    holdersIssues and PRs regarding holders

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions