Skip to content

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

Closed
@EricCousineau-TRI

Description

@EricCousineau-TRI

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

class A {
 public:
  A(int value)
      : value_(value) {}
  int value() 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>
struct holder_helper {
static auto get(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);
}

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